Instruction-Level Parallelism and its Dynamic Exploitation Κεφάλαια 4o Computer Architecture-A Quantitative Approach 3 rd Edition
Basic Pipeline Για την αποφυγή ενός pipeline stall πρέπει µια εξαρτώµενη εντολή να απέχει από την εντολή πηγή τόσα clock cycles όσα και το pipeline latency αυτής Στη συνέχεια θεωρούµε τα latencies του FP unit του σχήµατος όπως επίσης Integer Pipeline 5 επιπέδων Branches delay 1 clock cycle Εντολές κάθε είδους µπορούν να εκτελούνται σε κάθε clock cycle No Structural Hazards
Παράδειγµα Έστω ότι προσθέτουµε σε ένα διάνυσµα σταθερά s for (i=1000; i>0; i=i-1) x[i] = x[i] +s; Loop: L.D ADD.D S.D DADDUI BNE F0, 0 (R1) F4, F0, F2 F4, 0 (R1) R1, R1, # -8 R1, R2, Loop ; F0=array element ; add scalar in F2 ; store result ; decrement pointer ; 8 bytes (per DW) ; branch R1!= R2
Παράδειγµα Χωρίς scheduling το loop θα εκτελούνταν όπως παρακάτω: Loop: L.D stall ADD.D stall stall S.D DADDUI F0, 0 (R1) F4, F0, F2 F4, 0 (R1) R1, R1, # -8 Clock cycle issued 1 2 3 4 5 6 7 stall BNE stall R1, R2, Loop 8 9 10 Οκώδικας χρειάζεται 10 clock cycles ανά επανάληψη
Παράδειγµα Μπορούµενα µειώσουµε τα stalls σε 1 οργανώνοντας το loop Loop: L.D DADDUI ADD.D stall BNE S.D F0, 0 (R1) R1, R1, # -8 F4, F0, F2 R1, R2, Loop F4, 8 (R1) ; delayed branch ; altered and interchanged with DADDUI Ο χρόνος εκτέλεσης µειώνεται σε 6 clock cycles Το stall υπάρχει για την S.D Παρατηρούµε ότι για να οργανωθεί το loop έπρεπε για την ανταλλαγή του DADDUI µε το S.D να αλλάξει η διεύθυνση αποθήκευσης του S.D. (0(R1) 8(R1)) Αυτή η αλλαγή δεν είναι τετριµµένη και εξαρτάται από τον compiler
Loop Unrolling Στην προηγούµενη υλοποίηση µονάχα 3 εντολές σχετίζονται µε τον πίνακα Στόχος: να αυξήσουµε τις εντολές που σχετίζονται µε branch και overhead εξαλείφοντάς τα. Loop: L.D ADD.D S.D L.D ADD.D S.D L.D ADD.D S.D L.D ADD.D S.D DADDUI BNE F0, 0 (R1) F4, F0, F2 F4, 0 (R1) F6, -8 (R1) F8, F6, F2 F8, -8 (R1) F10, -16 (R1) F12, F10, F2 F12, -16 (R1) F14, -24 (R1) F16, F14, F2 F16, -24 (R1) R1, R1, # -32 R1, R2, Loop ; drop DADDUI & BNE ; drop DADDUI & BNE ; drop DADDUI & BNE 14 εντολές+ 4 L.D stalls+2*4 ADD.D stalls+1 DADDUI stall+1 branch stall= 28 clock cycles 7 clock cycles για κάθε στοιχείο
Loop unrolling µε scheduling Loop: L.D L.D L.D L.D ADD.D ADD.D ADD.D ADD.D S.D S.D DADDUI S.D BNE S.D F0, 0 (R1) F6, -8 (R1) F10, -16 (R1) F14, -24 (R1) F4, F0, F2 F8, F6, F2 F12, F10, F2 F16, F14, F2 F4, 0 (R1) F8, -8 (R1) R1, R1, # -32 F12, -16 (R1) R1, R2, Loop F16, -24 (R1) Σύνολο 14 clock cycles 3.5 clock cycles για κάθε στοιχείο
Loop unrolling Problems? Register pressure Χρειάζονται αρκετοί registers Περισσότεροι registers Έξυπνη χρήση των registers για µείωση Original Loop Scheduled Loop Unrolled loop Scheduled Unrolled loop Granularity Τι γίνεται αν το loop count δεν είναι πολλαπλάσιο του 4; εν γνωρίζουµε γενικά το loop count σε compile time Cycles/Iteration 10 6 7 3.5 Code movement ύσκολο για τον compiler να γνωρίζει αν µια µετακίνηση είναι επιτρεπτή
Τα dependences µειώνουν την παραλληλία Data dependence (RAW hazards) Μία εντολή χρειάζεται το αποτέλεσµα άλλης εντολής add r3,r2,r1 sub r4,r3,r5 sw 0(r1),r2 lw r5,10(r3) Name dependence (WAR, WAW hazards) ύο εντολές χρησιµοποιούν το ίδιο όνοµα αλλά δεν ανταλλάσσουν δεδοµένα add r3,r2,r1 sub r2,r4,r5 add r4,r1,r2 mult r4,r6,r7 Control dependence (control hazards) Μία εντολή ελέγχου καθορίζει αν µία εντολή εκτελείται beqz r3,location sub r2,r4,r5 add r4,r1,r2 bnez r9,loop
Μείωση των dependences µε χρήση Unrolling (1) Η SD µπορεί να µετακινηθεί µετά τις DADDUI και BNE Μειώνει WAR name dependence Οι επαναλήψεις loop είναι ανεξάρτητες Επιτρέπει στο loop unrolling να προσθέσει ανεξάρτητες εντολές στο block Χρήση διαφορετικών καταχωρητών για κάθε unrolled επανάληψη Μειώνει WAW και WAR name dependences Loop: L.D L.D L.D L.D ADD.D ADD.D ADD.D ADD.D S.D S.D DADDUI S.D F0, 0 (R1) F6, -8 (R1) F10, -16 (R1) F14, -24 (R1) F4, F0, F2 F8, F6, F2 F12, F10, F2 F16, F14, F2 F4, 0 (R1) F8, -8 (R1) R1, R1, # -32 F12, -16 (R1) BNE R1, R2, Loop S.D F16, -24 (R1)
Μείωση των dependences µε χρήση Unrolling (2) Αποµάκρυνση των επιπλέον εντολών DADDUI και BNE Μειώνει RAW και control dependences Αναδιοργάνωση loads/stores Ανάλυση διευθύνσεων µνήµης για εξασφάλιση µη ύπαρξης data dependences Scheduling του κώδικα ιατήρηση των υπόλοιπων dependences Loop: L.D L.D L.D L.D ADD.D ADD.D ADD.D ADD.D S.D S.D DADDUI F0, 0 (R1) F6, -8 (R1) F10, -16 (R1) F14, -24 (R1) F4, F0, F2 F8, F6, F2 F12, F10, F2 F16, F14, F2 F4, 0 (R1) F8, -8 (R1) R1, R1, # -32 S.D F12, -16 (R1) BNE R1, R2, Loop S.D F16, -24 (R1)
Static Branch Prediction Ανάγκη στατιστικής πρόβλεψης των εντολών διακλάδωσης κατά τη µεταγλώττιση Μέθοδοι Predict a branch as taken Predict on the basis of branch prediction Predict branches on the basis of profile information collected from earlier runs Instructions between mispredicted branches καλύτερη µονάδα µέτρησης (metric) από το misprediction
Static Branch Prediction Ακρίβεια predicted-taken στρατηγικής και profiled based predictor για SPEC92 benchmarks βάσει µετρήσεων του αριθµού των εντολών που εκτελούνται ανάµεσα από mispredicted branches.
VLIW Πολλαπλά ανεξάρτητα functional units Πολύ µεγάλη εντολή που περιλαµβάνει πολλαπλές λειτουργίες Παράδειγµα VLIW επεξεργαστής µε: Εντολές µε 5 λειτουργίες 1 integer operation 2 FP operations 2 memory references 16-24 bits ανά unit µήκος εντολής 112 168 bits Πρέπει να υπάρχει αρκετή παραλληλία σε µία λίστα κώδικα για να χρησιµοποιούνται τα διαθέσιµα slots λειτουργιών
Παράδειγµα Έστω VLIW µε δυνατότητα εκτέλεσης σε ένα κύκλο µηχανής 2 αναφορών στη µνήµη, 2 FP λειτουργίες και 1 integer ή branch λειτουργία. Ζητάµε µια unrolled έκδοση του loop x[i]=x[i]+s έτσι ώστε να µην υπάρχουν stalls. Αγνοούµε το delay slot του branch 7 αποτελέσµατα σε 9 κύκλους 1.29 κύκλοι ανά αποτέλεσµα 60% των functional units χρησιµοποιήθηκαν
VLIW Τεχνικά Προβλήµατα Αυξηµένος κώδικας Unrolling loops Σπαταλούµενα µη χρησιµοποιούµενα bits Περιορισµοί σε lockstep operation Ένα stall σε κάποιο functional unit έχει σαν αποτέλεσµα να µείνει ανενεργός ολόκληρος ο επεξεργαστής Λογιστικά Προβλήµατα Binary code compatibility ιαφορετικός αριθµός units και unit latencies απαιτεί διαφορετικό κώδικα
Loop-Level Parallelism Καθορισµός των εξαρτήσεων που υπάρχουν ανάµεσα στα operands σε ένα loop κατά τις επαναλήψεις αυτού του loop Data Dependences: ένα operand γράφεται σε κάποιο σηµείο και διαβάζεται σε µεταγενέστερο σηµείο Loop-carrier dependence: Data accesses σε µεταγενέστερες επαναλήψεις εξαρτώνται από τιµές που παράχθηκαν σε προγενέστερη επανάληψη Αν δεν υπάρχει loop-carrier dependence τότε έχουµε loop-level parallel περίπτωση
Loop-Level Parallelism for (i=0; i<1000; i++) x[i] = x[i] +s; Loop: L.D ADD.D S.D DADDUI BNE F0, x (R1) F4, F0, F2 X(R1), F4 R1, R1, # 8 R1, R2, Loop Παρατηρούµε διαφορές ανάµεσα στο source code και την assembly To DADDUI δηµιουργεί νέα τιµή που χρησιµοποιείται στην επόµενη επανάληψη εν είναι προφανές στο source code Ποιες είναι οι name, control και data dependences? H ανάλυση συνήθως γίνεται κοντά στο source code
Παράδειγµα Έστω loop: for (i=1; i<=100; i=i+1){ A[i+1]= A[i] + C[i]; /* S1 */ B[i+1]= B[i] + A[i+1]; /* S2 */ } Θεωρούµε ότι για τα A, B, C δεν υπάρχει overlapping στη µνήµη Κάτι τέτοιο θα ήταν δύσκολο να καθοριστεί από τον compiler Data dependences To S1 χρησιµοποιεί µία τιµή του S1 από προηγούµενη επανάληψη To S2 χρησιµοποιεί µία τιµή του S2 από προηγούµενη επανάληψη (Αν υπήρχαν µόνο αυτές οι εξαρτήσεις θα ήταν loop-carried) To S2 χρησιµοποιεί την τιµή A[i+1] που υπολογίζεται από το S1 στην τρέχουσα επανάληψη (Αν υπήρχε µόνο αυτή η εξάρτηση ΕΝ θα ήταν loop-carried)
Έστω loop: for (i=1; i<=100; i=i+1){ Παράδειγµα A[i]= A[i] + Β[i]; /* S1 */ B[i+1]= C[i] + D[i]; } /* S2 */ To S1 χρησιµοποιεί το προϊόν προηγούµενου υπολογισµού του S2 loop-carrier dependence. Όµως αυτό δεν εµποδίζει να γίνει παράλληλο το loop επειδή αυτή η εξάρτηση δεν είναι κυκλική. Αφού η εξάρτηση δεν είναι κυκλική µπορούµε να ανταλλάξουµε τις δύο εντολές χωρίς να επηρεαστεί η εκτέλεση του S2. Στην πρώτη εκτέλεση η τιµή του S1 εξαρτάται από το Β[1] Έτσι έχουµε το εξής non loop-carried loop A[1]=A[1]+B[1]; for (i=1; i<=99; i=i+1){ B[i+1]= C[i] + D[i]; A[i+1]= A[i+1] + Β[i+1]; ) B[101]=C[100]+D[100];
Βρίσκοντας και ελαχιστοποιώντας τα dependences Γιατί είναι σηµαντικό Καλό scheduling του κώδικα Καθορισµός των loops που µπορούν να παραλληλοποιηθούν Ελαχιστοποίηση των name dependences Απλό για αναφορές σε µόνες µεταβλητές ύσκολο για pointers, arrays, κλπ Η εστίαση είναι στα loops Ξεδίπλωµα (unroll) όσες περισσότερες resources επιτρέπονται Χρήση διαφορετικών ονοµάτων registers για κάθε επανάληψη και επαναπρογραµµατισµός για µεγιστοποίηση του παραλληλισµού
for (i=0; i<100; i++){ A[i]= B[i] + C[i]; D[i]= A[i] + E[i]; } Βρίσκοντας τον παραλληλισµό No loop carried dependences Εφαρµόζουµε unroll στο loop και βρίσκουµε την παραλληλία Πρέπει να σεβαστούµε το RAW του A[i] Χρειάζεται να φορτώσουµε το A[i] από τη µνήµη; Εύκολο να το δούµε στην συγκεκριµένη περίπτωση Συνήθως πολύ δυσκολότερο
Αναδροµές for (i=2; i<100; i++){ Υ[i]= Υ[i-1] + Y[i]; } Αναδροµή Η µεταβλητή ορίζεται βασιζόµενη στην τιµή της σε προγενέστερη επανάληψη Κάποιες αρχιτεκτονικές (vector processors) υποστηρίζουν τον χειρισµό αναδροµών Μπορεί να είναι καλή πηγή παραλληλισµού for (i=6; i<100; i++){ Υ[i]= Υ[i-5] + Y[i]; } Εφαρµόζουµε unroll 5 φορές για να πάρουµε 5 ανεξάρτητες εντολές
Τεχνικές του Compiler για αύξηση του ILP Loop Unrolling Χρησιµοποιούνται διάφορες επαναλήψεις του loop για να βρεθεί η παραλληλία Software pipelining Τροποποίηση του loop body για εύρεση της παραλληλίας Trace Scheduling Πρόβλεψη των branches και συνδυασµός block Speculation Χρησιµοποιείται υποστήριξη hardware για υποθέσεις σχετικά µε την κατάληξη του branch
Software Pipelining Symbolic loop unrolling Τεχνική αναδιοργάνωσης των loop κατά την οποία κάθε επανάληψη αποτελείται από εντολές επιλεγµένες από διαφορετικές επαναλήψεις του αρχικού loop
Symbolic Unrolling Original Code Unrolled Code L.D F0,0(R1); ADD.D F4,F0,F2; S.D F4,0(R1); DADDUI R1,R1,#-8 BNE R1,R2,Loop Iteration i: L.D F0,0(R1) ADD.D F4,F0,F2 S.D F4,0(R1) Iteration i+1: L.D F0,0(R1) ADD.D F4,F0,F2 S.D F4,0(R1) Iteration i+2: L.D F0,0(R1) ADD.D F4,F0,F2 S.D F4,0(R1)
Software Pipelined Code Original Code Software Pipelined Code L.D F0,0(R1); ADD.D F4,F0,F2; S.D F4,0(R1); DADDUI R1,R1,#-8 BNE R1,R2,Loop S.D F4,16(R1) ;Stores to M[i] ADD.D F4,F0,F2;Adds to M[i-1] L.D F0,0(R1) ;Loads M[i-2] DADDUI R1,R1,#-8 BNE R1,R2,Loop 10 cycles ανά iteration (6 αν είναι rescheduled) 5 cycles ανά iteration (µε DADDUI στο branch delay slot)
Software Pipelining Symbolic Loop Unrolling Maximize result-use distance Less code space Fill & drain pipe µόνο µια φορά ανά loop vs. µία φορά για κάθε unrolled επανάληψη στο loop unrolling
Global Code Scheduling Στοχεύει στο να ενσωµατώσει ένα µέρος κώδικα σε µια εσωτερική δοµή ελέγχου στην συντοµότερη πιθανή ακολουθία που διατηρεί τα data και control dependencies Απαιτεί πολύπλοκα trade-offs για αποφάσεις µετακίνησης κώδικα τα οποία εξαρτώνται από πολλούς παράγοντες ύο µέθοδοι που απλοποιούν τη διαδικασία Trace Scheduling (Σηµαντικές διαφορές στη συχνότητα διαφορετικών paths) Trace Selection (Αναζητά ακολουθία βασικών block trace) Trace Compaction (Μάζεµα του trace σε όσο το δυνατόν µικρότερο αριθµό ευρέων εντολών) Βασικό µειονέκτηµα: Πρόβληµα σε περίπτωση εξόδου από τη µέση του trace Superblocks (Μοναδική είσοδος- πολλαπλές έξοδοι)
Trace Scheduling - Superblocks
Υποστήριξη Hardware για περισσότερο ILP Conditional or Predicated Instructions Αποφυγή branch prediction µετατρέποντας τα branches σε conditionally executed instructions: if (x) then A=B op C else NOP Αν false τότε δεν αποθηκεύεται το αποτέλεσµα ούτε προκαλείται exception MIPS, Alpha, PowerPC, SPARC, Intel x86 υποστηρίζουν conditional move. IA-64 όλες οι εντολές conditional Μειονεκτήµατα ιαρκεί ένα clock ακόµη κι αν ακυρωθεί (annuled) Αν η συνθήκη υπολογιστεί αργά έχουµε stall Complex conditions µειώνουν την αποτελεσµατικότητα γιατί η συνθήκη αναγνωρίζεται αργά από το pipeline
Υποστήριξη Hardware για περισσότερο ILP Speculation Εκτέλεση εντολής χωρίς συνέπειες (συµπεριλαµβάνοντας exceptions) αν το branch τελικά δεν εκτελεστεί Συνδυασµός branch prediction και dynamic scheduling για εκτέλεση πριν το branch resolving Μειονέκτηµα: Πολυπλοκότητα και τα πρόσθετα απαιτούµενα resources Hardware Speculation Aids Αγνοεί τα terminate exceptions Poison bits Boosting
Intel IA-64 Instruction Set Architecture IA-64 Register model 128 64-bit GPR, (actually 65 bits) 128 82-bit FPR, (2 extra bits over 80-bit IEEE format) 641-bit predicate registers 8 64-bit branch registers (για indirect branches) Registers for control, memory mapping, performance counters, communication with the OS Implicit Parallelism (instruction groups) Fixed formatting (bundle)
Execution Unit types of Intel IA-64 Intructions bundle 5-bit template field+3 instructions *41-bits=128 bits
υνατά formats που κωδικοποιούνται στο template field
Instruction Set Basics High order 4 bits + bundle bits execution unit slot Low order 6 bits καθορίζουν τον predicate register που κρατά την εντολή
Predication- Speculation Σχεδόν κάθε εντολή µπορεί να είναι predicated Predicate registers που γίνονται set χρησιµοποιώντας compare ή test εντολές Speculation Control speculation (πραγµατεύεται deferred exceptions για speculated εντολές) Memory reference speculation (Speculation των εντολών load)
Itanium Processor Πρώτη υλοποίηση της ΙΑ-64 αρχιτεκτονικής (800 MHz) Μέχρι 6 issues/clock Μέχρι 3 branches και 2 memory references 3-level caches FP data όχι στο πρώτο επίπεδο cache 9 functional Units 2 I-units 2 M-units 3 B-units 2 Funits
Itanium Pipeline 10-stage pipeline µε 4 βασικά µέρη Front-end (stages IPG, Fetch και Rotate) 32 bytes/clock Instruction delivery (stages EXP και REN) Κατανέµονται µέχρι 6 εντολές σε 9 functional units Operand delivery (WLD και REG) Πρόσβαση στο Register file, έλεγχος predicate dependences Execution (EXE, DET και WRB) Εκτέλεση εντολών, ανίχνευση exceptions, write back
Σύγκριση Itanium µε Alpha, Pentium µε SPECint Itanium: 800Mhz, 4MB off-chip L3 cache Alpha: 1GHz, on-chip L2 cache Pentium: 2GHz, 256KB on-chip L2 cache
Σύγκριση Itanium µε Alpha, Pentium µε SPECfp Itanium: 800Mhz, 4MB off-chip L3 cache Alpha: 1GHz, on-chip L2 cache Pentium: 2GHz, 256KB on-chip L2 cache
Trimedia TM32 Κλασσική embedded VLIW αρχιτεκτονική Κάθε εντολή 5 λειτουργίες Επεξεργαστής Statically Scheduled
Transmeta Crusoe Processor VLIW επεξεργαστής σχεδιασµένος για την low-power αγορά, όπως mobile PCs και συσκευές mobile internet Συµβατός µε x86 αρχιτεκτονική µέσω software συστήµατος µετάφρασης x86 εντολών σε VLIW εντολές του Crusoe In-order execution 64-bits (2 operations) ή 128-bits (4 operations) εντολές Power consumption for the workload (W) Workload description Mobile Pentium III @ 500 MHz,1.6V TM3200 @400MHz 1.5V Relative consumption TM3200/Mobile PentiumIII MP3 playback 0.672 0.214 0.32 DVD playback 1.13 0.479 0.42