Wegen dem Speicherinterface. Da eine CPU Karte zu entwerfen, die über dedizierten, aufgelöteten, hoch getakteten Speicher mit meist geringen Latenzen um im Anschluss doch nur wieder ein Problem mit Bandbreite und Latenzen des Speichers zu haben leuchtet mir nicht ein. Das Vorgehen wäre schon arg merkwürdig und wird normalerweise durch abartig große L2 und L3 Caches behoben. Die lassen sich auf dem Dieshot kaum ausmachen, daher vermute ich, dass das kein all zu großes Problem sein wird.
Es gibt eben unterschiedliche Lösungsstrategien um die Latenzen, welche durch Speicherzugriffe und Register bzw. Pipelingeabhänigkeiten entstehen, zu verbergen. Zusätzlich sind CPUs und GPUs Agglomerationen von verschiedenen Ausführungseinheiten für verschiedene Befehle.
Eine Nvidia GPU besitzt:
LSUs (SpeicherBefehle) // Cuda Kerne für FP und AL //TMUs (Texturbefehle) //SFUs (Transzendente Funktionen)
Ein Prozessor besitzt:
Speichercontroller,ALUs,FPUs
Diese Ausführungseinheiten sollen ebenfalls optimal ausgelastet werden.
Diese beiden Probleme sind miteinander verzahnt, und müssen somit zusammen betrachtet werden. Es gibt für sie verschiedene Lösungsstrategien:
Komplexere Pipeline insbesondere:
-Out of Order Execution: Befehle werden nicht in der Reihenfolge ausgeführt, wie sie dastehen, sondern in derjenigen Reihenfolge wie es wohl am meisten Performance bringt. Im Nachhinein wird das Ergebnis wieder richtig zusammengesetzt, so als ob der Code in der richtigen Reihenfolge ausgeführt worden ist. Dies verbirgt Registerabhängigkeiten Speicherlatenzen und sorgt für eine bessere Auslastung der verschiedenen Ausführungseinheiten. Denn dadurch kann der Befehl an eine Ausführungseinheit sofort gegeben werden, wenn die Ausführungseinheit frei ist und die Operranden zur Verfügung stehen. Ohne Out of Order Execution wären die Ausführungseinheit so lange unbelegt bis der Befehl als nächstes im Code drankommen würde.
-Branch Prediction: Der Prozessor rät im vorneherein, welchen Branch bei if else etc. das Programm nehmen wird. Wird ein anderer Branch genommen, werden die Ergebnisse nach dem Sprungbefehl verworfen.
Diese Pipelineoptimierung findet vor allem auf CPUs statt. Denn sie benötigt viel Chipfläche und bringt damit hauptsächlich bei sequentiellen Berechnungen etwas. Auf GPUs hat man deshalb eine einfache Load Operrands/Execute/Store Operrands Pipeline für die Rechenkerne.
SMT:
Für jeden Rechenkern werden mehrere Threads gleichzeitig beherbergt. Diese befinden sich komplett in den Registern des Prozessors, wodurch das Wechseln zwischen den Threads kostenlos ist. Dadurch kann sofort wenn einer der Threads wegen Latenzen gerade nicht weiterrechnen kann, ein anderer Thread weiterrechnen. Auch sind mehrere Threads vorteilhaft um die einzelnen Ausführungseinheiten für die unterschiedlichen Befehle besser auszulasten. Denn dadurch hat man mehr Befehle, welche man als nächstes Ausführen könnte, wodurch die Wahrscheinlichkeit erhöht wird, dass für alle Ausführungseinheiten befehle dabei sind.
SMT benötigt je nach Umfang weniger Chipfläche als eine komplexere Pipeline, bringt aber nur bei parallen Aufgaben etwas, da man darauf angewiesen ist viele Threads zu haben. Deshalb kommt es hauptsächlich bei Parallelrechnern zur Anwendung.
Ein GPU Multiprozessor beherbergt 8-16 Threads pro Cuda Kern, um die Latenzen zu verstecken. Man benötigt allerdings bei der primitiven Pipeline der GPU mindestens 3 Threads um die Registerabhängigkeiten bei den Cuda Kernen zu verstecken. Wenn man noch Speicherlatenzen vertuschen Will benötigt man noch wesentlich mehr Threads.
Eine CPU haust 1-2 und KNC 4 Threads.
Cache:
Der Hauptspeicher wird bei Zugriffen im Cache zwischengespeichert, wodurch nur(!) Speicherlatenzen verborgen werden und der Hauptspeicher entlastet wird. Registerabhängigkeiten werden nicht verborgen; die Auslastung der Ausführungseinheiten nicht erhöht. Die Effizienz des Caches ist zusätzlich davon abhängig, dass man lokale Speicherzugriffe hat, was nicht in jeder Anwendung gegeben ist. Bei weitestgehend lokalen Speicherzugriffen hat man ausserdem das Problem, dass je mehr Cache man hat, umso weniger Performancesteigerung lässt sich durch zusätzlichen Cache erzielen. So belegt der L1 Cache nur sehr wenig Chipfläche, bietet aber enormen Performance gewinn von vermutlich mehreren 100 % (Leider keine Benchmarks verfügbar). Der L2 Cache ist schon grösser und bringt nur noch viel weniger Performance gewinn; sagen wir einmal 50 %. Der L3 Cache belegt schon ein drittel Der Chip Fläche, bringt allerdings nur noch ~5-10 % Performance (Siehe zB
http://www.tomshardware.com/reviews/athlon-l3-cache,2416-9.html)
Deshalb verwendet man grosse Cache nur bei sequentiellen Problemen, bei denen man darauf angewiesen ist, dass wenige Threads möglichst schnell abgearbeitet werden. Bei Parallelrechnern wählt man den Cache deshalb wesentlich kleiner und versucht die Latenzen durch SMT zu verbergen.