Arbeitsblatt #2

Werbung
Arbeitsblatt #2
Einführung in die Systemprogrammierung SS 2013
10. Mai 2013
Abgabedatum: 17:00, 17.05.2013
In dieser Übung vertiefen wir die MIPS-Programmierung und beschäftigen uns mit einigen für
Performanz kritischen Komponenten.
1
Taktfrequenz und Ausführungszeit [5.3, 5.5] ( 12 Punkt)
Die Kennwerte CPI (Zyklen pro Instruktion) bzw. IPC (Instruktionen pro Zyklus) werden meist
in Bezug auf ein gegebenes Programm berechnet. IPC ist dann die durchschnittliche Anzahl von
Instruktionen, die in einem Prozessorzyklus asugeführt wird, und CPI der Kehrwert.
IPC und CPI werden oft verwendet, um abzuschätzen, wie gut ein bestimmtes Stück Code optimiert
ist. Auf modernen Systemen gilt dabei ein IPC-Wert knapp unter 1.0 Instruktionen pro Zyklus als
Indiz für gute Optimierung.
Wir verwenden diese Untersuchung nun für MIPS-Code. Die folgende MIPS-Befehlssequenz wird
auf einem eingebetteten Prozessor ausgeführt, der Teil einer Gerätesteuerung ist:
main:
la $t0 , buffer
addi $t2 , $t0 , 0 x4000
loop:
lw $t1 , 0( $t0)
andi $t1 , $t1 , 0x0c
sw $t1 , 0( $t0)
addi $t0 , $t0 , 4
bne $t0 , $t2 , loop
# Messung beginnt
# Messung endet
Wir messen zwischen den beiden Kommentaren eine Laufzeit von 10ms, bei einer geschätzten Standardabweichung von 0.1ms (unter Annahme einer Gaußschen Normalverteilung).
Wir wissen folgendes über das System:
• Der Prozessor ist ein älterer MIPS-Prozessor
• Der Prozessor verwendet das fünf-Phasen-Pipelining aus der Vorlesung, aber keine superskalare
Ausführung
• Speicherzugriff benötigt nur einen Taktzyklus
• Der Prozessor ist mit 3 MHz getaktet
Nicht alle Informationen sind notwendigerweise für die folgenden Aufgaben relevant.
a. Berechnen Sie den IPC-Wert für das obige Programm.
b. Geben Sie mindestens einen plausiblen Grund dafür an, daß die Standardabweichung nicht 0 ist.
1
2
Optimierungen [5.1, 5.4] (1 Punkt)
Sie haben ein Computerspiel entwickelt, das eine virtuelle Welt simuliert. Mehrmals pro Sekunde
wird dem Spieler eine zweidimensionale Projektion der dreidimensionalen Sicht auf die virtuelle Welt
darstellt, daneben werden auch Geräusche abgestpielt.
Ihr erster Prototyp ist nun fertig, aber das Programm läuft aus Ihrer Sicht zu langsam: gemäß
Ihren Messungen kann selbst bei voller Auslastung ihres hochmodernen Rechnersystems die Sicht
auf die virtuelle Welt nur 10 mal pro Sekunde aktualisiert werden. Sie schätzen, daß mindestens 30
Aktualisierungen pro Sekunde nötig sind, damit das Programm schnell genug läuft.
Jede Aktualisierung wird von einem Aufruf einer Routine ‘update’ berechnet. Ein wesentlicher Teil
dieser Routine ist die Kommunikation mit einem Audio-Subsystem (zuständig für die Klangverarbeitung), von dem Sie vermuten, daß es sehr ungeschickt implementiert ist. Tatsächlich ergeben Ihre
Messungen, daß update volle 10ms pro Aufruf allein mit Klangverarbeitung verbringt.
a. Welcher Speedup für die Routine update ist nötig, damit das Programm so schnell läuft, wie Sie
es sich wünschen?
b. Was ist der maximale Speedup, den Sie durch Verbesserung der Klangverarbeitung erreichen
können?
c. Beschreiben Sie Ihre nächsten Schritte, um mit möglichst wenig Aufwand Fortschritt in Richtung
des gewünschten Speedups zu machen.
3
Pipeline-Ausführung [6.1, 6.2] (1 Punkt)
Betrachten Sie das folgende Assemblerprogramm:
nop
nop
nop
nop
nop
#
#
#
#
#
Der ‘nop ’- Befehl hat keine Wirkung
[00 F0]
[00 F4]
[00 F8]
[00 FC]
start :
ori
lui
ori
lw
add
sw
nop
nop
nop
nop
$t0 ,
$t1 ,
$t1 ,
$t2 ,
$t2 ,
$t2 ,
$zero , 2
0 x00a0
$ti , 0 x1a80
0( $t1)
$t2 , $t0
0( $t1)
# [0118]
# [011C]
# [0120]
# [0124]
#
#
#
#
#
#
[0100]
[0104]
[0108]
[010C]
[0110]
[0114]
Wir führen dieses Programm auf dem 5-Phasen-Pipelineprozessor aus der Vorlesung aus.
a. Beschreiben Sie ab der Sprungmarke start, was die Inhalte der verschiedenen Phasen nach
jedem Zyklus sind. Falls Sie die Tabelle auf der nächsten Seite dazu verwenden, überspringen
Sie dabei die Zeilen, die mit Durchreichen markiert sind.
b. Falls Registerwerte von den Zwischenregistern durchgereicht wreden müssen, markieren Sie dies
durch Pfeile in der Tabelle in den mit Durchreichen markierten Zeilen. Verbinden Sie dabei die
Trennlinien, die an der Position der durchreichenden Register in der Tabelle stehen. Geben Sie
den Namen des betroffenen Registers an.
Falls Sie die Übung nicht auf Papier einreichen, beschreiben Sie das Durchreichen, indem Sie die
Zwischenregister angeben, zwischen denen durchgereicht wird, z.B. ‘DS/RS $t0 nach ID/AUS
durchgereicht’.
2
ID
AUS
DS
RS
Reg
PSp
00FC
00F8
ALU
ALU
IL
DSp
Reg
00F4
00F0
00EC
Durchreichen:
Durchreichen:
Durchreichen:
Durchreichen:
Durchreichen:
Durchreichen:
Durchreichen:
Durchreichen:
Durchreichen:
Durchreichen:
Durchreichen:
4
Taktfrequenz und Ausführungszeit: Optimierungen [6.2] ( 12 Punkt)
Kehren Sie zurück zur ersten Aufgabe dieses Übungsblattes. Ändern Sie die Schleife dort ab, so daß
sie weniger Taktzyklen zur Ausführung benötigt.
5
Verzögerte Verzweigung [6.4] (1 Punkt)
Gegeben sei ein MIPS-Prozessor unbekannter Version. Schreiben Sie ein Programm, um herauszufinden, ob dieser Prozessor verzögerte Verzweigung verwendet oder nicht. Diese Eigenschaft soll alleine
an der Ausgabe des Programmes erkennbar sein. Führen Sie das Program unter WebSPIM aus.
a. Verwendet dieser Prozessor verzögerte Verzweigung?
b. Erklären Sie, wie und warum die Ausgabe Ihres Programmes dies belegt.
6
Sortierter Binärbaum [4] (6 Punkte)
Ein sortierter Binärbaum mit 32 Bit-Zahlen ist ein gerichteter azyklischer Graph G = hV, E1 , E2 , W, i
der aus Knoten V besteht, mit einem designierten Wurzelknoten ∈ V . Die partiellen Funktionen E1 , E2 : V → V⊥ bilden Knoten auf ihre Kindknoten ab (sofern diese existieren), während die
Wertfunktion W : V → Int32 jeden Knoten auf eine vorzeichenbehaftete 32-Bit-Zahl in Zweierkomplementdarstellung, den Wert des Knotens, abbildet.
3
Dabei gilt die Invariante I1 , daß
I1 : E1 (v) = v 0 ⇒ W (v 0 ) < W (v)
also Kinder über E1 nur auf Knoten mit geringerem Wert, und die Invariante I2 , daß
I2 : E2 (v) = v 0 ⇒ W (v 0 ) > W (v)
also Kinder über E2 nur auf Knoten mit höherem Wert abgebildet werden.
Zum Beispiel:
Wurzel 5
-3
7
6
Der Wurzelknoten im obigen Beispiel hat den Wert 5 und zwei Kindknoten. Der linke Kindknoten
(über E1 ) hat den kleineren Wert −3 und selbst keine Kindknoten. Der rechte Kindknoten (über E2 )
des Wurzelknotens wiederum hat den Wert 7 und einen weiteren Kindknoten (über E1 ), mit dem
Wert 6.
In dieser Aufgabe implementieren wir Binärbäume in MIPS-Assembler, allerdings ohne den üblichen
Balancierungsschritt.
Es gibt viele Möglichkeiten, Mengen wie V und (partielle) Funktionen wie E1 , E2 , W im Speicher
abzubilden. In der Praxis ist es oft am effizientesten, Funktionen zu sammeln, die alle die gleiche
Definitionsmenge (V , in diesem Fall) haben, und deren Abbildungen für jedes Element der Definitionsmenge zusammenzufassen. In höheren Sprachen geschieht dies in Datenstrukturen; in Assembler
simulieren wir diese Datenstrukturen durch programmspezifische Konventionen.
In unserem Fall können wir also für jeden Knoten v die Abbildungen E1 (v), E2 (v), W (v) zusammen
speichern; die Repräsentierung des Knotens v im Speicher entspricht also einem Tripel dieser drei
Abbildungen.
Wir repräsentieren dabei Werte W (v) als normale Zweierkomplementzahlen, aber es ist weniger
offensichtlich, wie wir Kindknoten E1 (v), E2 (v) repräsentieren sollten, und es existieren verschiedene
Lösungen für dieses Repräsentierungsproblem.
Die häufigste Lösung ist die, daß Kindknoten v 0 durch ihre Speicheradressen repräsentiert werden.
Da die Repräsentierung der Knoten Platz im Speicher einnimmt, können wir die Adressen dieses
Speichers als Bezeichner für den betreffenden Knoten verwenden. Wenn also ein Kindknoten v 0 ab
Adresse 0x2000 im Speicher liegt (und z.B. den Wert W (v 0 ) von Adresse 0x2000 bis einschließlich
0x2003 speichert), dann kann der Elternknoten v sich den Kindknoten v 0 merken, indem er sich eine
der Adressen von v 0 merkt; per Konvention ist dies meist die kleinste Adresse, also 0x2000 in unserem
Beispiel.
Die folgenden Teilaufgaben können Sie separat oder in Kommentaren innerhalb Ihres Programmes
beantworten.
a. Folgen Sie den obigen Ausführungen und entwerfen Sie eine Speicherbelegung für Knoten des
Graphs, so daß jeder Knoten eine konstante Menge an Speicher einnimmt. Wieviele Bytes nimmt
jeder Knoten ein?
b. Beschreiben Sie für jedes Byte Ihrer Speicherbelegung, welche Bedeutung dieses Byte bzgl. des
Graphen G hat. (Wenn mehrere Bytes in Folge eine verwandte Bedeutung haben, können sie
diese zusammen beschreiben und müssen nicht jedes Byte einzeln ausführen.) Wie behandeln sie
Situationen, in denen dem Knoten ein oder mehrere Kindknoten fehlen?
c. Schreiben Sie den Rumpf eines MIPS-Programmes, das mit Knoten Ihres Baumes arbeitet.
Allozieren Sie dazu genug Platz im statischen Speicher (.data), um mindestens 100 Knoten
aufzunehmen.
4
Schreiben Sie eine Subroutine alloc node, die die Adresse des nächsten freien Knotens in Ihrem
statischen Speicher zurückliefert. Jeder Aufruf dieser Routine sollte also eine ‘frische’ Adresse
als Ergebnis zurückliefern und sicherstellen, daß an der gegebenen Adresse genug Platz ist, um
einen Baumknoten zu speichern.
Was passiert, wenn Ihr alloc node häufiger aufgerufen wird, als Platz verfügbar ist?
d. Entscheiden Sie sich für eine Repräsentierung des Wurzelknotens und beschreiben Sie diese.
e. Schreiben Sie die Hauptroutine der Baumbehandlung, eine Subroutine find node. Ihre Routine
sollte einen Parameter w nehmen und vom Wurzelknoten aus den Knoten v finden, der diesen
Wert w hat.
Beachten Sie dabei für jeden Knoten v 0 , den Sie besuchen, folgendes:
• Wenn W (v 0 ) = w, dann haben Sie den korrekten Knoten gefunden.
• Wenn W (v 0 ) > w, dann müssen Sie im Kindknoten E1 (v 0 ) weitersuchen, sofern dieser
existiert.
• Wenn W (v 0 ) < w, dann müssen Sie im Kindknoten E2 (v 0 ) weithersuchen, sofern dieser
existiert.
Falls kein Knoten v mit W (v) = w existiert, soll die Routine stattdessen den letzten besuchten
Knoten v 0 zurückliefern.
f. Schreiben Sie eine Routine has value, die einen Parameter (einen Wert) nimmt und überprüft,
ob dieser Wert in dem Baum gespeichert ist. Die Routine soll 1 zurückliefern falls der Wert im
Baum abgelegt ist, ansonsten 0.
Verwenden Sie die vorher implementierten Routinen nach Bedarf, um Arbeit zu sparen.
g. Schreiben Sie eine Routine add value, die einen Parameter (einen Wert) nimmt und überprüft,
ob dieser Wert in dem Baum gespeichert ist. Falls nicht, fügt die Routine den Wert in einem
neuen Knoten in den Baum ein. Stellen Sie sicher, daß die Routine die Invarianten I1 und I2
gewährleistet!
Verwenden Sie die vorher implementierten Routinen nach Bedarf, um Arbeit zu sparen.
h. Schreiben Sie ein Hauptprogramm (main), das Zahlen vom Benutzer einliest und in Ihren
Binärbaum schreibt, bis der Benutzer die Zahl 0 eingibt (die nicht geschrieben wird). Für alle
danach eingegebenen Zahlen schauen Sie nach, ob die Zahl im Binärbaum gespeichert wurde
oder nicht, und geben eine entsprechende Meldung aus.
5
Herunterladen