superscalar und einem vliw prozessor
-
hi,
was ist der unterschied zwischen einen superscalar und einem vliw prozessor, abgesehen das man beim superscalar zur laufzeit bestimmt, welche befehle in welcher funktionseinheit landen, was beim vliw prozessor zur compilezeit passiert?
was ist besser?bye
-
folgendes verstehe ich nicht;/
- ein superskalarer Prozessor speist seine Ausführungseinheiten aus nur einem Befehlsstrom einfacher Befehle,
- bei einem VLIW-Prozessor ein Befehlsstrom von VLIW-Befehlspaketen, also von Tupeln einfacher Befehle
-
Wie definierst Besser? Auf jeden Fall haben sich VLIW-Prozessoren nicht durchgesetzt, wie der Intel Itanium.
-
naja besser kommt wohl aufs anwendungsgebiet an;)
was heisst folgendes?
- ein superskalarer Prozessor speist seine Ausführungseinheiten aus nur einem Befehlsstrom einfacher Befehle,- bei einem VLIW-Prozessor ein Befehlsstrom von VLIW-Befehlspaketen, also von Tupeln einfacher Befehle
all diese einfachen befehle im tupel werden parallel ausgeführt oder werden da tupel parallel ausgeführt? wo ist der unterschied zu superskalar?cu
-
Superscalar: Komplexität wird auf den Prozessor übertragen (Prozessor verwaltet Parallelisierung zur Laufzeit)
VLIW: Komplexität wird auf den Compiler übertragen (Compiler verwaltet Parallelisierung zur Compilezeit)
Was ist einfacher? Frage einen Compilerentwickler und einen Hardwareentwickler und du wirst 2 verschiedene Antworten bekommen. Was ist zur Laufzeit effizienter? Potenziell würde ich sagen VLIW, da zur Laufzeit weniger "Schalten" notwendig ist. Letzten Endes sollte es aber hauptsächlich auf die Implementierung ankommen.
-
superman1 schrieb:
naja besser kommt wohl aufs anwendungsgebiet an;)
was heisst folgendes?
- ein superskalarer Prozessor speist seine Ausführungseinheiten aus nur einem Befehlsstrom einfacher Befehle,- bei einem VLIW-Prozessor ein Befehlsstrom von VLIW-Befehlspaketen, also von Tupeln einfacher Befehle
all diese einfachen befehle im tupel werden parallel ausgeführt oder werden da tupel parallel ausgeführt? wo ist der unterschied zu superskalar?das bedeutet, dass beim superskalar prozessor der ganze code befehl fuer befehl konsumiert wird, beim vliw wird in bloecken . was besser ist, ist wie du schon gesagt hast, anwendungsgebiet abhaengig. das ist wie cisc vs risc.
fuer ganz simplen code ist cisc+superscalar+outoforder am besten, weil der prozessor quasi generischen opcode schlucken muss und entsprechend der einheiten und ihren spezifischen latencies und throughputs und auch abhaengig von externen einfluessen (z.b. ram latenz oder cache), die einheiten am flexibelsten nutzen kann.
wenn du hand optimierst bzw dir genau ansiehst was der compiler macht auf der untersten ebene, dann wird risc interesant. es gibt viele kleine dinge die viel angenehmeren code fuer den prozessor generieren, aber niemand schaut nach um das zu sehen und es ist auch inpraktikabel das fuer den ganzen code zu machen. ein beispiel
//copy u16* pIn... u16* pOut... for(int a=0;a<N;a++) pOut[a]=pIn[a];
der gcc generierste code in pseudo assembler (jede c zeile ist ein assembler befehler quasi, nur verstaendlicher) fuer den ARM ist dann in etwa
a=0 m: InAddresse=pIn+a OutAddresse=pOut+a InAddresse*=2 OutAddresse*=2 kopiere ein word in TempRegister aus [OutAddresse] kopieren ein word in [InAddresse] aus Tempregister a++ vergleiche a gegen 100 falls kleiner, branche/springe zu zeile m
bevor ich jetzt alles in details ausfuehre und dich zutode langweile schreibe ich es mal die finale version (sorry fuer eventuelle fehler, ist sicher schon 5jahre her dass ich das letzte mal fuer ARM schrieb)
u8* pIn=(u8*)... u8* pOut=(u8*)... u8* pOutEnd=pOut+200 do *(u16*)pOut=*(u16*)pIn; pOut++; pInt++; whie(pOut!=pOutEnd)
in der output ist dann
pOut m: kopiere ein word in TempRegister aus [OutAddresse] ; OutAddresse+=2; kopieren ein word in [InAddresse] aus Tempregister ; InAddresse+=2; vergleiche pOut gegen pOutEnd falls kleiner, branche/springe zu zeile m
wie du siehst, kann der ARM in einem befehl dinge umkopieren und den index um die groesse des elements das man kopiert incrementieren, aber simple addressberechnung waere dann fatal.
Du siehst,dein code kann 2 bis 3mal schneller werden indem du deinen c code umstellst nachdem du dir den assembler anschaust und das gilt fuer risc.nun mal VLIW, das ist sozusagen multi-unit risc. du hast in einem opcode strang n-streange "versteckt". ich nehme mal als beispiel hardware eine SPU, das ist simple VLIW mit 2 straengen (auch wenn es dem "very long" entgegen steht
)
der decoder nimmt also immer 64bit (32bit pro befehl) und steckt sie in die ausfuehrungseinheiten. dabei muss er 1. sie immer in der richtigen reihenfolge ausfuehren und 2. sind die befehle immer 32bit aligned.
die 2 pipelines werden "odd" und "even" genannt und koennen ca die haelfte der befehle jeweils, wobei jeder befehl exklusiv pro pipeline ist. eine genaue auflistung findest du hier
machen wir es simpel und sagen, die linke kann rechnen und logik (ala or) und die rechte kann load/store und branching.
hast du z.b. sowas wieload add load load add add store compare branch
im decoder kommt folgendes an
load add load load add add store compare branch ..
auf die einheiten verteilt also
___ load add ___ ___ load ___ load add ___ add ___ ___ store ___ compare ___ branch
du siehst, massive gaps in der pipe, nun wagen wir mal ein experiment und fuegen etwas unsinniges ein
nop load add load load add add store compare branch
im decoder kommt folgendes an
nop load add load load add add store compare branch
auf die einheiten verteilt also
nop load add load ___ load add ____ add store ___ compare ___ branch
du siehst, es ist nicht zwingend intuitiv was man machen muss um eine instruktion schneller zu werden, du siehst aber auch, x-instruktionen spaeter hat das noch einfluss und paired add und store, aber genau so kann es sein dass es solche instruktionen un-paired. der compiler muss also neben dem generieren von moeglichst effektivem code auch noch
1. die packete effektiv zusammenstellen wobei jedes packet auch potentiell die nachfolgenden beeinflusst
2. register usage nicht nur zwischen den einzelnen instructions, sondern auch zwischen den packeten tracken und optimieren
3. beim zusammenstellen von packeten die latencies der einzelen befehle beachten in bezug auf die vorherigen packete und befehle und die nachfolgenden packete und befehle
...
und jetzt nimm noch branching dazu etc.und um jetzt kein buch zu schreiben, mal kurz was ein static code analyser sagt wenn man 08/15 c code compiliert
251 0x00000b10 and $5,$6,$78 0x00000b14 lnop 252 0x00000b18 il $6,128 0x00000b1c shufb $9,$3,$8,$60 253 0x00000b20 ila $8,0x10203 0x00000b24 lqd $3,256($127) 254 255 256 0x00000b28 stqx $9,$11,$38 257 0x00000b2c lqx $10,$80,$39 258 0x00000b30 lqx $9,$11,$39 259 260 261 262 263 0x00000b34 rotqby $87,$10,$88 264 265 266 267 0x00000b38 shufb $88,$87,$9,$59 268 269 270 271 0x00000b3c stqx $88,$11,$39 272 0x00000b40 lqx $2,$80,$40 273 0x00000b44 lqx $10,$11,$40 274 275 276 277 278 0x00000b48 rotqby $38,$2,$89 279 280 281
wie du siehst, benutzt der compiler moeglichst viele register um register presure nicht beim optimieren beachten zu muessen, und versucht mit nops (erste zeile) die pipeline gut zu fuellen, aber einfach aufgrund der art und weise wie der code geschrieben wurde gibt es eine extreme abhaengigkeit der einzelnen befehle und dann kann der compiler nicht sonderlich viel machen ohne sich auf gefaehrlichen boden zu bewegen.
wenn man dann noch die latenz der einzelnen befehle sieht, sieht man dass man sie auch locker haette nacheinander ausfuehren koennen ohne dass etwas grossartig ineffizienter geworden waere. (und ich habe mir keinen besonderen code rausgesucht, sondern einfach mal runtergescrollt und was kopiert).und mit hand optimierten code nach ner woche arbeit
-inlining von allem was da ist
-eliminierung jeglichen if/else
-spu instrinsics
-vectorisierung55 0x00001bd0 il $83,12 0x00001bd4 rotqmby $7,$32,$60 56 0x00001bd8 fm $33,$59,$3 0x00001bdc shlqby $10,$29,$124 57 0x00001be0 il $32,4 0x00001be4 shlqby $28,$23,$17 58 0x00001be8 sfi $124,$27,0 0x00001bec rotqmby $6,$14,$122 59 0x00001bf0 ai $23,$58,4 0x00001bf4 shlqby $18,$105,$27 60 0x00001bf8 or $93,$4,$10 0x00001bfc shufb $35,$113,$24,$98 61 0x00001c00 or $45,$7,$28 0x00001c04 shufb $68,$19,$19,$51 62 0x00001c08 il $105,20 0x00001c0c shufb $81,$114,$20,$98 63 0x00001c10 or $44,$6,$18 0x00001c14 shufb $80,$112,$115,$98 64 0x00001c18 il $114,28 0x00001c1c rotqbyi $125,$87,8 65 0x00001c20 fma $88,$68,$35,$33 0x00001c24 shufb $15,$93,$93,$51 66 0x00001c28 il $113,24 0x00001c2c shufb $30,$45,$45,$51 67 0x00001c30 ai $28,$58,8 0x00001c34 shufb $31,$44,$44,$51 68 0x00001c38 ai $68,$58,20 0x00001c3c rotqbyi $79,$93,4 69 0x00001c40 fm $37,$15,$3 0x00001c44 rotqbyi $86,$45,4 70 0x00001c48 fm $85,$30,$3 0x00001c4c shufb $96,$81,$80,$8 71 0x00001c50 fm $34,$31,$3 0x00001c54 shufb $39,$125,$125,$51 72 0x00001c58 ai $31,$58,12 0x00001c5c rotqbyi $123,$44,4 73 0x00001c60 il $125,40 0x00001c64 rotqbyi $63,$87,12 74 0x00001c68 nop $127 0x00001c6c shufb $82,$79,$79,$51 75 0x00001c70 fma $50,$39,$96,$88 0x00001c74 shufb $65,$86,$86,$51 76 0x00001c78 ai $86,$58,36 0x00001c7c shufb $36,$123,$123,$51 77 0x00001c80 il $123,36 0x00001c84 rotqbyi $38,$93,8 78 0x00001c88 fma $91,$82,$35,$37 0x00001c8c rotqbyi $40,$45,8 79 0x00001c90 fma $47,$65,$35,$85 0x00001c94 shufb $57,$81,$80,$98 80 0x00001c98 fma $46,$36,$35,$34 0x00001c9c shufb $100,$63,$63,$51 81 0x00001ca0 ai $34,$58,24 0x00001ca4 rotqbyi $69,$44,8 82 0x00001ca8 il $65,52 0x00001cac shufb $43,$38,$38,$51 83 0x00001cb0 ai $38,$58,28 0x00001cb4 shufb $48,$40,$40,$51 84 0x00001cb8 fma $55,$100,$57,$50 0x00001cbc lqd $54,1104($127) 85 0x00001cc0 ai $100,$58,60 0x00001cc4 shufb $94,$69,$69,$51 86 0x00001cc8 fma $53,$43,$96,$91 0x00001ccc rotqbyi $49,$93,12
der code wurde ca 50mal schneller insgesammt. manches kann man dem compilier nicht abgewoehnen und mit hand geschriebenem spu assembler bekommt man noch ca 10% mehr raus.
wenn man VLIW code sieht mit 6 pipes die alle was unterschiedliches machen, ist die ausbeute meistens sehr arm und der compiler kann wirklich nicht viel machen, selbst bei super geschriebenem code. ich habe hier fuer manche dinge compiler die 6minuten an ca 100 opcodes werkeln und dann trotzdem kein optimales resultat liefern. Ich habe mit compilerschreiben geredet die nichts anderes als VLIW compiler jahrelang schrieben und die meinen sie moegen VLIW nicht wirklich, aus dem simplen grund weil die optimierung so complex von sovielen knappen resourcen abhaengen, dass es nichtmal simple ist einen static code analyser zu schreiben und dafuer dann noch zu optimieren ist ab nem gewissen grad eher ein for(a=0 to 100){randomize_instructions;static_profile;save_if_better;}
und so mancher code der rauskommt ist erstmal unerklaerlich und man involviert die halbe support firma bis dann jemand ne erklaerung hat wieso der compiler etwas macht wie er es macht und die chancen sind ca 50:50 dass er es schlauer gemacht hat als es jeder mensch sieht oder dass es ein weiterer sonderfall ist den man dem compiler beibringen muss.ich hoffe meine kleine exkusrion hat alle klarheiten beseitigt
-
hi,
ein noch paar fragen zum vliw (very long instruction word) processor:
- was heisst "explicite parallelism"?
- kurze, unabhängige befehle werden zu einem langen befehlswort = 1 maschinenbefehl zusammengepackt.
werden dann alle befehle synchron abgearbeitet?
- um n befehle in einem befehlspaket gleichzeitig abzuarbeiten brauch ich n funktionseinheiten oder?
- warum hat der vliw eine einfacherer hardware als der superscalar proc? weil der superscalar die zuweisung der befehle zur laufzeit machen muss?lg
-
superman1 schrieb:
- was heisst "explicite parallelism"?
weil
werden dann alle befehle synchron abgearbeitet?
- um n befehle in einem befehlspaket gleichzeitig abzuarbeiten brauch ich n funktionseinheiten oder?
so sollte es sein
- warum hat der vliw eine einfacherer hardware als der superscalar proc? weil der superscalar die zuweisung der befehle zur laufzeit machen muss?
zuweisung ist nicht das problem, VLIW weist ja auch zu.
decodieren und verteilen ist aufwendiger, gerade wenn die instruktionsgroesse dynamisch ist. dafuer werden n-bytes gelesen und an jeder bytestelle wird spekulativ dekodiert und am ende vom decoder wird dann erst selektiert welche der ganzen dekodierten befehle die richtige sequenz haben. beim in order werden die befehle dann auf die einzelnen pipelines gelegt und zum richtigen zeitpunkt, wie bei VLIW zugewiesen.jedoch sind die decoder, obwohl sie komplex klingen, meist ein sehr kleiner teil der prozessoren. daran aendert sich meist auch wenig und die auslastung ist bei kritischen applikationen auch eher gering weil dort die befehle im post-decode buffer meist fuer die kritischen schleifen reichen.
entsprechend haben nahalem CPUs als stromspar mittel die moeglichkeit den decoder kurzzeitig abzuschalten. (wenn ich es richtig gelesen habe).
-
- was heisst "explicite parallelism"?
weilwas meinst du mit weil?
-
"explicit parallelism" heißt, dass bei VLIW im Befehlscode, der vom Compiler erstellt wird, explizit die parallel abzuarbeitenden Befehle angegeben sind.
-
Bei dynamisch superskalaren Prozessoren stehen keine Parallelisierungsinformationen im Befehlscode. Deshalb nutzt beispielsweise die x86-Architektur das auch. Obwohl erst später Superskalareigenschaften hinzugefügt wurden, ist der Befehlscode trotzdem zu nicht-superskalaren Prozessoren der gleichen Architektur abwärtskompatibel, und es kann alter Code weiterhin verwendet werden.