24.11.2013 Aufrufe

2. Aufgabenblatt – Musterlösung

2. Aufgabenblatt – Musterlösung

2. Aufgabenblatt – Musterlösung

MEHR ANZEIGEN
WENIGER ANZEIGEN

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

<strong>2.</strong> <strong>Aufgabenblatt</strong> <strong>–</strong> <strong>Musterlösung</strong><br />

Technische Informatik II <strong>–</strong> Sommersemester 2011<br />

Problem 2: Assembler Syntax<br />

Register eines 32-bit Intel-Prozessors:<br />

⟵ 16 bits ⟶<br />

⟵ 8 bits ⟶<br />

⟵ 8 bits ⟶<br />

general purpose registers<br />

EAX Accumulator Register AH AL AX<br />

EBX Base Register BH BL BX<br />

ECX Counter Register CH CL CX<br />

EDX Data Register DH DL DX<br />

ESI<br />

EDI<br />

Source Index<br />

Destination Index<br />

EBP<br />

ESP<br />

Base Pointer<br />

Stack Pointer<br />

⟵ 32 bits ⟶<br />

Weiterführende Informationen zu den Registern und deren Verwendung finden sich in<br />

The Art of Picking Intel Registers (http://www.swansontec.com/sregisters.html)<br />

Hallo-Welt-Programm in C “hello-world.c”<br />

#include "stdio.h"<br />

int main(int argc, int **argv) {<br />

printf("%s\n", "Hello World!");<br />

return 0;<br />

}


“gcc -m32 -S hello-world.c”, Inhalt von “hello-world.s”<br />

.LC0:<br />

.file "hello-world.c"<br />

.section .rodata<br />

.string "Hello World!"<br />

.text<br />

.globl main<br />

.type main, @function<br />

main:<br />

pushl %ebp<br />

movl %esp, %ebp<br />

andl $-16, %esp<br />

subl $16, %esp<br />

movl $.LC0, (%esp)<br />

call puts<br />

movl $0, %eax<br />

leave<br />

ret<br />

.size main, .-main<br />

.ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"<br />

.section .note.GNU-stack,"",@progbits<br />

Vergleichbares hello-world-Programm, handgeschrieben (AT&T-Syntax)<br />

.section .data<br />

hello: .ascii "Hello World!\n"<br />

.section .text<br />

.globl _start<br />

_start:<br />

mov $4, %eax<br />

mov $1, %ebx<br />

mov $hello, %ecx<br />

mov $13, %edx<br />

int $0x80<br />

mov $1, %eax<br />

mov $0, %ebx<br />

int $0x80<br />

# 4 fuer den Syscall 'write'<br />

# File Descriptor<br />

# Speicheradresse des Textes<br />

# Laenge des Textes<br />

# syscall<br />

# Exit-Code setzen<br />

# Funktion 0 (exit) auswählen<br />

# syscall<br />

Vergleichbares hello-world-Programm, handgeschrieben (Intel-Syntax)<br />

.intel_syntax noprefix<br />

.section .data<br />

hello: .ascii "Hello World!\n"<br />

.section .text<br />

.globl _start<br />

_start:<br />

mov eax, 4<br />

mov ebx, 1<br />

mov ecx, offset hello<br />

mov edx, 13<br />

int 0x80<br />

mov eax, 1<br />

mov ebx, 0<br />

int 0x80<br />

# 4 fuer den Syscall 'write'<br />

# File Descriptor<br />

# Speicheradresse des Textes<br />

# Laenge des Textes<br />

# syscall<br />

# Exit-Code setzen<br />

# Funktion 0 (exit) auswählen<br />

# syscall


Problem 3: Recherchieren Sie (Bewertete Aufgabe)<br />

Hinweis: Statt der Summe das Produkt zu berechnen, würde die Fakultät berechnen, 5000! sprengt ein 32-<br />

bit Register aber um ein Vielfaches. Daher bleiben wir hier bei der Summe.<br />

Auf dem Institutsrechner andorra benötigt die handgeschriebene Assembler-Funktion grob<br />

5000 CPU-Zyklen, die kompilierte C-Version hingegen um die 30000.<br />

Kompiliert man das Programm hingegen mit der stärksten Optimierung (gcc -O3 -m32<br />

assembler.S framework.c), benötigt die kompilierte C-Version nur noch knapp 200 Zyklen.<br />

Woran liegt das?<br />

Betrachtet man diese simple Variante der gauss-Summe:<br />

int main() {<br />

int a = 5000;<br />

int x = 0;<br />

for (; a; a--) {<br />

x += a;<br />

}<br />

return x;<br />

}<br />

und lässt sich die generierte Assembler-Datei einmal mit und einmal ohne Optimierung<br />

anzeigen, so sieht man die folgenden beiden Programme:<br />

ohne Optimierung -O3<br />

.file "gauss-super-simple.c"<br />

.text<br />

.globl main<br />

.type main, @function<br />

main:<br />

leal 4(%esp), %ecx<br />

andl $-16, %esp<br />

pushl -4(%ecx)<br />

pushl %ebp<br />

movl %esp, %ebp<br />

pushl %ecx<br />

subl $16, %esp<br />

movl $5000, -12(%ebp)<br />

movl $0, -8(%ebp)<br />

jmp .L2<br />

.L3:<br />

movl -12(%ebp), %eax<br />

addl %eax, -8(%ebp)<br />

subl $1, -12(%ebp)<br />

.L2:<br />

cmpl $0, -12(%ebp)<br />

jne .L3<br />

movl -8(%ebp), %eax<br />

addl $16, %esp<br />

popl %ecx<br />

popl %ebp<br />

leal -4(%ecx), %esp<br />

ret<br />

.size main, .-main<br />

.file "gauss-super-simple.c"<br />

.text<br />

.p2align 4,,15<br />

.globl main<br />

.type main, @function<br />

main:<br />

leal 4(%esp), %ecx<br />

andl $-16, %esp<br />

pushl -4(%ecx)<br />

movl $12502500, %eax<br />

pushl %ebp<br />

movl %esp, %ebp<br />

pushl %ecx<br />

popl %ecx<br />

popl %ebp<br />

leal -4(%ecx), %esp<br />

ret<br />

.size main, .-main<br />

Die Unterschiede sind lila hervorgehoben.<br />

In der linken Variante erkennt man die<br />

Schleife. Da sie aber immer das gleiche<br />

ausgeben wird (12502500 nämlich), wurde<br />

in der optimierten Variante diese Schleife<br />

vom Compiler direkt durch den Wert<br />

ersetzt.


TI II - Uebung 2<br />

Felix Herter, Moritz Niklas Paul<br />

May 12, 2011<br />

1 Aufgabe 3<br />

Kompiliert man die framework.c erhält man für die Berechnung der Gaußschen Summe von n = 5000 die<br />

Taktzahlen:<br />

Assembler: 10944<br />

C: 60780<br />

Der geringere Rechenaufwand der Assemblerfunktion ist leicht zu erkennen, auch wenn das auf dem Heimrechner<br />

(Intel(R) Core(TM)2 CPU <strong>2.</strong>00GHz ) erzielte Verhältnis von ca. 1:6 auf den Uni Rechnern nicht zu reprodizieren<br />

war (Verhältnis von ca. 1:2).<br />

Die folgenden Codeauszüge zeigen die geänderten Stellen des Ursprungsprogramms, sodass sie nun die Fakultät<br />

anstelle der Gaußschen Summe ausgeben.<br />

Assembler:<br />

mov %eax, %ecx<br />

dec %ecx<br />

1:<br />

imul %eax, %ecx<br />

dec %ecx<br />

jnz 1b<br />

C: int fak(int a){<br />

int x=1<br />

for(;a;a--){<br />

x*=a;<br />

}<br />

return 0;<br />

So weit so gut, die entsprechende Ausgabe für die Berechnung der Fakultät von n = 13 liefert folgende Taktzahlen:<br />

Assembler: 708<br />

C: 960<br />

Auch hier ist der Assembler schneller, wenn auch nicht mehr ganz so eindeutig wie bei der Summenberechnung,<br />

welche jedoch auch mit einem deutlich größeren Parameter aufgerufen wurde.<br />

Kompilieren wir nun die framework.c zusätzlich mit der Optimierungsflag -O3, so erwarten wir, dass der C-<br />

Code schneller laufen wird als der Assembler-Code. Auf jeden Fall zumindest schneller, als vor dem Setzen der<br />

Flag. Die Ausgabe hingegen straft unsere Naivität mit den Werten:<br />

1


Assembler: 577723137680999036<br />

C: 166200<br />

Allein ein Blick auf die angebliche Anzahl der benötigten Takte, welche die Assemblerfunktion benötigt haben<br />

soll zeigt, dass es hier wohl nicht mit rechten Dingen zugehen kann: Bei den 2GHz des benutzten Prozessors<br />

ist es Recht unwahrscheinlich, dass in den Sekundenbruchteilen, die die Berechnung gedauert hat, über 5 · 10 17<br />

Takte stattgefunden haben. . . Leider war es auch auf anderen Rechnern (Netbook, Unirechner) nicht möglich<br />

zufriedenstellende Ergebnisse zu erzielen.<br />

Aufgrund erheblich Zeitmangels (ALP2 Klausur) konnten wir uns nicht so ausgiebig mit den optimierten Assemblercodes<br />

auseinandersetzen (der des framework.c betrug 134 Zeilen(!)). Zur Vereinfachung haben wir eine<br />

separate fakultaet.c geschrieben, die die Fakultät von 13 ausgibt. Optimieren und dissassemblieren führten<br />

zu:<br />

.file "fakultaet.c"<br />

.section .rodata.str1.1,"aMS",@progbits,1<br />

.LC0:<br />

.string "13! = %i\n"<br />

.text<br />

.p2align 4,,15<br />

.globl main<br />

.type main, @function<br />

main:<br />

pushl %ebp #Basepointer wird auf den Stack gespeichert<br />

movl %esp, %ebp #Basepointer wird auf Stackpointer gesetzt (AT&T?)<br />

andl $-16, %esp #ist %esp==-16 ?<br />

subl $16, %esp<br />

movl $1932053504, 8(%esp) #Der Compiler hat den Wert bereits ausgerechnet(!)<br />

und speichert diesen in der Adresse, auf die %esp<br />

gerade Zeigt (reserviert 8 Speichereinheiten?)<br />

movl $.LC0, 4(%esp)<br />

movl $1, (%esp)<br />

call printf chk<br />

xorl %eax, %eax #effizient %eax auf 0 gesetzt?<br />

leave<br />

ret #%eax-Wert wird zurückgegeben?<br />

.size main, .-main<br />

.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"<br />

.section .note.GNU-stack,"",@progbits<br />

Unsere spartanischen Vermutungen stehen als Kommentar hinter den jeweiligen Anweisungen. Allerdings<br />

ist vielleicht noch erwähnenswert, dass der Compiler selbst anscheinen bereits gesehen hat, dass das Ergebnis<br />

1932053504 betragen wird, und dies nun einfach auf dem Stack speichert, anstatt die tatsächliche Implementierung<br />

der fakultaet.c im Assemblercode wieder zu spiegeln.<br />

2

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!