Mal etwas ausführlicher, inhaltliche Kritik oder Fragen nach ausführlicheren Erklärungen sind willkommen. Es war etwas problematisch das ganze inhaltlich "aufzurollen", da alles mit allem irgendwie zusammenhängt. Ich hoffe es ist mir dennoch halbwegs gut gelungen:
-Höherer Prallelismus, breiteres/anderes Hardware-Multithreading: Moderne GPUs benötigen je nach Anwendungsfall wegen ihrem Hardware-Multithreading mehrere 1000 Threads um gut ausgelastet zu sein (Titan kann maximal 29 000 Threads gleichzeitig bearbeiten, bei modernen AMD-Karten sind es 60k+ ), während Phi wenige 100 benötigt (Phi kann denke ich nur bis zu 300 Threads gleichzeitig bearbeiten). In den meisten Anwendungsgebieten ist der Parallelismus nicht aus dem Grund schädlich, weil man das Problem nicht genügend parallelisieren könnte, sondern aus anderen Gründen:
So benötigt man auf einer GPU wesentlich mehr Speicherplatz für die temporären Zwischenergebnisse aller Threads als auf einem PHI. Passt der Workingset der Zwischenergebnisse eines jeden Threads (auch die automatischen Variablen genannt) nicht mehr in die Register, müssen sie in in die Caches und den DRAM ausgelagert werden, was wiederum brutal Performance kostet. Dies wird als Register-Spilling bezeichnet. So kann man auf einer Titan beispielhaft schon mit Performanceeinbussen rechnen, wenn ein Thread mehr als 31 Zwischenergebnisse gleichzeitig zwischenspeichern muss. Des Weiteren führen mehr Threads unabhängig von Registerspilling auch zu chaotischeren globalen Speicherzugriffen (global = sämtlicher Speicher was sich alle Threads der GPU teilen wie Texturen, Vertexdaten, Hierarische Datenstrukturen), was wiederum der Cache-Effizienz schadet und brutal DRAM-Bandbreite kostet. Erschwerend kommt hinzu dass die GPU nur einen sehr kleinen L2-Cache besitzt (1.5 MB beim Titan), während der PHI einen sehr großen L2-Cache besitzt (Kernzahl* 2MB). Dadurch kann ein Algorithmus mit einem großen Working-Set für automatische und globale Variablen nicht mehr effizient auf einer GPU arbeiten. Meist kann man das zwar hinfriggeln, dass es irgendwie doch passabel oder eventuell sogar gut auf einer GPU funktioniert, was aber sehr viel Arbeit ist.
Allerdings ist diese Art des Hardware-Multithreading der GPU auch fallabhängig von Vorteil. Denn der Grund dafür, dass man ein Hardware-Multithreading einbaut ist immer der, dass man dadurch Latenzen besser überbrücken kann und die unterschiedlichen (Rechen-)Ressourcen besser auslasten kann. Dies kann eine GPU wegen dem breiteren Multithreading dementsprechend auch besser als ein Xeon-Phi.
-SIMD: Ein Warp ist eine Gruppe von N Threads auf der GPU (N = 32 bei NVIDIA, 64 bei AMD). Eine GPU führt jeden Takt jeweils die nächste Instruktion eines Warps aus, wodurch ein und der selbe Befehl jeweils auf den unterschiedlichen Daten eines jeden Warpthreads ausgeführt wird (Single Instruction Multiple Data). Wollen mehrere Threads eines Warps wegen einem Sprungbefehl nicht an einem Befehl teilnehmen, so bleiben in diesem Fall die entsprechenden Rechenkerne unbenutzt und es geht Rechenleistung verloren. Dadurch reagieren GPUs sehr empfindlich auf Algorithmen mit chaotischen Sprungbefehlen. In den meisten Fällen kann man zwar die Sprungbefehle GPU-freundlich optimieren, was allerdings wiederum ein großes Gefriggel ist. Xeon-Phi besitzt ebenfalls SIMD-Instruktionen (eine Instruktion auf 16 SP-Werte gleichzeitig), jedoch beziehen sie sich auf die Daten eines einzelnen Threads. Hier muss man zwar den Quelltext auch etwas umgestalten, dass der Compiler diese SIMD-Instruktionen verwenden kann und Sprungbefehle können sich ebenfalls per Active-Mask, welche beschreibt welche der Daten überhaupt am SIMD-Befehl teilnehmen, negativ auf die Performance auswirken, aber afaik fallen die nachteiligen Auswirkungen von Sprungbefehlen auf die Performance wesentlich geringer als bei GPUs aus.
-Shared Memory: GPUs haben einen kleinen Scratch-Pad-Speicher auf dem DIE (ähnlich zu dem eDRAM auf den Konsolen). Diesen kann man nach belieben befüllen und auslesen, und dadurch die zu kleinen Caches etwas kompensieren. Allerdings ist es in den meisten Fällen ein großes Gefriggel den zu verwenden.
-Besondere Caches auf der GPU: GPUs haben besondere Read-Only-Caches (Texture-Cache, Konstanten-Cache), deren dazugehörige Daten während der gesamten Programmausführung nicht verändern dürfen. Diese Caches muss man explizit und "richtig" verwenden, was einen Mehraufwand bei der Programmierung bedeutet, jedoch aus Performancegründen wesentlich ist. Gleichzeitig muss man dafür sorgen, dass sich die Daten nicht verändern, was die Programmierbarkeit einschränkt.
-Vielzahl von Hardwarelimitierungen: Eine GPU besitzt für viele Aufgaben Spezialhardware. Verwendet man diese, was wiederum in den meisten Fällen empfehlenswert ist, so muss man die dazugehörigen Hardware-Limitierungen in Kauf nehmen. Stören diese Limitierungen bzw. stößt man an deren Grenzen so muss man *irgendwie* beim Programmieren darauf eingehen.
-Occupancy: Während bei dem Hardware-Multithreading auf Prozessoren jeder Thread seinen eigenen Registersatz hat (also bei 4 Fach bei einem PHI-Kern) teilen sich die Threads eines Multiprozessors auf der GPU einen Registersatz. Je mehr Register ein Thread auf der GPU benötigt, desto weniger Threads passen in einem Multiprozessor bzw. desto weniger Threads kann die GPU gleichzeitig bearbeiten. Wie bereits erwähnt, will man jedoch meist möglichst viele Threads gleichzeitig aktiv haben, um die Latenzen überbrücken zu können und die GPU gut auszulasten. So ist es in den meisten Fällein ein großes Gefriggel die Occupancy gut hinzubekommen. Auf einem PHI existiert das Konzept der Occupancy und die daraus resultierenden Probleme nicht.
-Keine Interrupts auf GPUs in Kombination mit Hardware-Scheduling: Ein Thread rechnet auf einer GPU so lange bis er terminiert; terminiert er nicht so rechnet er ewig (oder bis Windows den Displaytreiber zurücksetzt). Er kann sich auch nicht schlafen legen, die Kontrolle an einen anderen Thread abgeben, um auf ein bestimmtes Ereignis zu warten. Wenn er dennoch auf ein Ereignis warten muss, so muss er aktiv ständig die Bedingung abfragen, was Rechenleistung verschwendet. Diverse Anwendungen (wie Betriebssysteme) setzen auch voraus, dass man einen Thread per Interrupt unterbrechen kann. Diese Anwendungen sind auf der GPU ebenfalls nicht verwirklichbar. Auf einem PHI stellen sich wegen Interrupts diese Probleme nicht.
-x86 Architektur: Gerade bei kleineren Rechenaufgaben mit größerer und älterer x86-Code-Base mag es keinen Sinn machen sich einen Programmierer einzustellen, der das in mühevoller kleinarbeit auf die GPU portiert. Da mag es sehr vorteilhaft sein, sich einfach einen PHI zu kaufen und auf diesen den x86 Code direkt ausführen zu können.