Parallele Prozessoren und Mehrkernprozessoren
MittelEinheit 6 von 6 in Kurs: Rechnerarchitektur – Prozessoren, Speicher und RISC-V
Was du lernst
- Erkläre, warum Prozessoren parallel wurden, unterscheide Befehls-, Daten-, Thread- und Anfrageparallelität, klassifiziere Rechner anhand der flynnschen Taxonomie und beschreibe, wie Hardware-Multithreading einen Prozessorkern auslastet.
- Erkläre den Übergang zu parallelen Rechnern und benenne die Arten der ParallelitätStelle den Zusammenhang zwischen der Power Wall und dem Übergang zu Mehrkernchips her und erkenne Befehls-, Daten-, Thread- und Anfrageparallelität in realen Programmen.
- Klassifiziere parallele Hardware anhand der flynnschen TaxonomieOrdne einen Rechner anhand der Anzahl seiner Befehls- und Datenströme SISD, SIMD, MISD oder MIMD zu und erkläre den SPMD-Programmierstil.
- Laste einen Prozessorkern mit Hardware-Multithreading ausErkläre, wie feingranulares, grobgranulares und simultanes Multithreading Wartezeiten verdecken und wie sich Hardwarethreads von zusätzlichen Kernen unterscheiden.
- Erkläre den Übergang zu parallelen Rechnern und benenne die Arten der Parallelität
- Berechne Speedup und Effizienz, wende das amdahlsche und das gustafsonsche Gesetz an, berücksichtige Lastungleichgewicht und Parallelisierungsaufwand und untersuche mit dem Roofline-Modell, ob die Rechenleistung oder die Speicherbandbreite ein Programm begrenzt.
- Berechne Speedup und Effizienz mit dem amdahlschen GesetzDefiniere Speedup und Effizienz, wende das amdahlsche Gesetz in beide Richtungen an und erkläre, warum der sequenzielle Anteil den Speedup eines Problems mit fester Größe begrenzt.
- Unterscheide starke und schwache Skalierung und berücksichtige den ParallelisierungsaufwandVergleiche den Speedup bei fester und bei wachsender Problemgröße, wende das gustafsonsche Gesetz an und schätze die Auswirkungen von Lastungleichgewicht, Kommunikation und Synchronisation ab.
- Bestimme mit dem Roofline-Modell eine obere Schranke für die RechenleistungBerechne die arithmetische Intensität, lies aus einem Roofline-Diagramm ab, ob ein Kernel durch die Speicherbandbreite oder die Spitzenrechenleistung begrenzt wird, und wähle entsprechende Optimierungen.
- Berechne Speedup und Effizienz mit dem amdahlschen Gesetz
- Verwende SIMD-Befehle und Vektorarchitekturen zur Verarbeitung von Arrays, erkläre den auf Durchsatz ausgelegten Aufbau einer GPU und schreibe und analysiere einen einfachen GPU-Kernel.
- Verarbeite mit SIMD-Befehlen mehrere Datenelemente gleichzeitigErmittle die Anzahl der Lanes eines SIMD-Registers, vektorisiere einfache Schleifen und erkenne die Datenanordnungen, Verzweigungen und Abhängigkeiten, die den SIMD-Speedup begrenzen.
- Erkläre, wie Vektorarchitekturen Schleifen ausführenBeschreibe Vektorregister, Vektorlänge, Strip-Mining, Lanes und Zugriff mit fester Schrittweite am Beispiel der RISC-V-Vektorerweiterung.
- Beschreibe, wie eine GPU für hohen Durchsatz aufgebaut istErkläre Streaming Multiprocessors, Warps und SIMT-Ausführung, die Verdeckung von Speicherlatenz durch GPUs, Kontrollflussdivergenz, Coalescing bei Speicherzugriffen und den Aufwand für Datentransfers. Vergleiche GPUs mit CPUs und domänenspezifischen Beschleunigern.
- Schreibe und analysiere einen einfachen GPU-KernelOrdne mithilfe der Hierarchie aus Grid, Block und Thread Daten den Threads zu, organisiere die Arbeit von Host und Gerät und synchronisiere innerhalb eines Blocks.
- Verarbeite mit SIMD-Befehlen mehrere Datenelemente gleichzeitig
- Beschreibe Multiprozessoren mit gemeinsamem Speicher und die Aufteilung der Arbeit auf Threads, erkläre die Garantien der Cachekohärenz, verfolge Snooping- und Verzeichnisprotokolle und vermeide False Sharing.
- Beschreibe Multiprozessoren mit gemeinsamem Speicher und verteile Arbeit auf die KerneErkläre einen gemeinsamen Adressraum, gleichförmigen und nicht gleichförmigen Speicherzugriff, den Cacheaufbau eines Mehrkernchips sowie die Aufteilung und Zusammenführung von Arbeit durch Threads.
- Erkläre das Cachekohärenzproblem und die Garantien der KohärenzZeige, wie private Caches veraltete Daten liefern können, nenne die Bedingungen für ein kohärentes Speichersystem und unterscheide Kohärenz von Konsistenz und von Code ohne Race Conditions.
- Verfolge ein Snooping-Protokoll mit MSI- und MESI-ZuständenVerfolge die Zustände von Cacheblöcken und Bustransaktionen bei einer Folge von Lese- und Schreibzugriffen und erkläre, welchen Aufwand der Exclusive-Zustand spart.
- Erkläre Verzeichnisprotokolle und vermeide False SharingBeschreibe, wie ein Kohärenzverzeichnis Mitnutzer erfasst, schätze seinen Speicherbedarf ab und erkenne, miss und behebe False Sharing.
- Beschreibe Multiprozessoren mit gemeinsamem Speicher und verteile Arbeit auf die Kerne
- Beschreibe Cluster mit Nachrichtenaustausch und vergleiche Verbindungsnetzwerke anhand von Bandbreite und Kosten. Erkläre anschließend, wie Warehouse-Scale Computer Internetdienste bereitstellen, und berücksichtige dabei Kosten, Energie und Ausfälle.
- Beschreibe Cluster mit Nachrichtenaustausch und vergleiche VerbindungsnetzwerkeErkläre, wie Knoten mit privaten Speichern durch Nachrichten zusammenarbeiten, und vergleiche Netzwerktopologien anhand von Gesamtbandbreite, Bisektionsbandbreite und Kosten.
- Erkläre, wie Warehouse-Scale Computer Dienste in großem Maßstab bereitstellenBeschreibe den Aufbau eines Warehouse-Scale Computers und untersuche sein Netzwerk, seinen Energieverbrauch und seine Ausfälle anhand einfacher Rechnungen.
- Beschreibe Cluster mit Nachrichtenaustausch und vergleiche Verbindungsnetzwerke
Mehrkernprozessoren einfach erklärt
1Parallele Arbeit finden und klassifizieren
Parallele Hardware kann Befehle, Arrayelemente, Threads oder unabhängige Anfragen bearbeiten. Die passende Wahl hängt davon ab, wo ein Programm Arbeit enthält, die gleichzeitig ausgeführt werden kann. Stelle den Zusammenhang zwischen dieser Arbeit und den Leistungsgrenzen her, die zum Einsatz mehrerer Kerne auf Chips führten, und klassifiziere anschließend die Hardware anhand ihrer Befehlsströme. Hardware-Multithreading bietet eine weitere Möglichkeit, einen Kern auszulasten, indem es während Wartezeiten nutzbare Befehle findet.
1.1 Warum Chips die Arbeit heute aufteilen
Parallele Rechner führen Operationen gleichzeitig aus. Sie können die Latenz, also die Dauer einer Aufgabe, senken oder den Durchsatz, die Anzahl der pro Sekunde abgeschlossenen Aufgaben, erhöhen. Diese Ziele führen zu unterschiedlichen Arten der Arbeitsaufteilung.
Die Power Wall
Früher machte eine höhere Taktfrequenz jede Prozessorgeneration schneller. Die dynamische Leistungsaufnahme folgt näherungsweise : Kapazität mal Spannung zum Quadrat mal Schaltfrequenz. Die Spannung geht in diesem Produkt doppelt ein. Senkt man Spannung und Frequenz jeweils um 15 %, ergibt sich der ursprünglichen dynamischen Leistungsaufnahme, also etwa 61 %.
Die Spannung ließ sich nicht weiter ausreichend senken, um höhere Taktfrequenzen auszugleichen. Leckströme und Wärme machten eine weitere Frequenzsteigerung zu aufwendig. Diese Power Wall führte die Branche Mitte der 2000er-Jahre zu Mehrkernchips. Ein Mehrkernchip vereint mehrere Prozessorkerne auf einem Chip; ein Multiprozessorsystem besitzt mindestens zwei Prozessoren. Einfachere Kerne können pro Joule insgesamt mehr Arbeit leisten, sofern genügend Arbeit zur Aufteilung vorhanden ist.
Vier Arten paralleler Arbeit
| Art | Was läuft gleichzeitig? | Beispiel |
|---|---|---|
| Befehlsparallelität (ILP) | Befehle aus einem Befehlsstrom | Eine Pipeline überlappt die Stufen aufeinanderfolgender Befehle. |
| Datenparallelität (DLP) | Dieselbe Operation auf vielen Elementen | Entsprechende Elemente zweier Arrays addieren. |
| Threadparallelität (TLP) | Mehrere Befehlsfolgen | Mehrere Threads bearbeiten unterschiedliche Bildbereiche. |
| Anfrageparallelität | Unabhängige Aufgaben | Server beantworten gleichzeitig Anfragen vieler Nutzer. |
ILP wird hauptsächlich von Hardware und Compilern erkannt. Pipelining, Mehrfachausgabe und Ausführung außerhalb der Programmreihenfolge überlappen Befehle und bewahren dabei die vorgeschriebenen Ergebnisse des Programms. Abhängigkeiten und Sprünge begrenzen die mögliche Überlappung.
DLP tritt auf, wenn viele Elemente dieselbe Berechnung benötigen. SIMD-Befehle, Vektorprozessoren und GPUs nutzen diese Regelmäßigkeit. Für TLP muss die Arbeit durch Code, eine Bibliothek oder einen Compiler auf Threads aufgeteilt werden. Ein Thread besitzt einen eigenen Befehlszähler, eigene Register und einen eigenen Stack; Threads eines Prozesses teilen sich dessen Adressraum. Getrennte Prozesse haben getrennte Adressräume.
Arbeit auf Anfrageebene benötigt wenig Zusammenarbeit zwischen den Aufgaben. Zusätzliche Server können die Kapazität daher nahezu proportional zu den hinzugefügten Ressourcen erhöhen. Auch mehrere unabhängige Programme auf einem Laptop nutzen sonst untätige Kerne und erhöhen den Gesamtdurchsatz, selbst wenn keines dieser Programme intern parallelisiert ist.
Nebenläufige Aufgaben und gleichzeitige Ausführung
Nebenläufigkeit bedeutet, dass Aufgaben während sich überschneidender Zeiträume Fortschritte machen. Ein Kern kann dies erreichen, indem er zwischen ihnen wechselt. Parallelität bedeutet, dass Operationen auf getrennter Hardware zum selben Zeitpunkt ausgeführt werden. Ein Einkernsystem kann mehrere Programme gleichzeitig in Bearbeitung halten, ohne ihre Befehle gleichzeitig auszuführen.
Ein Programm mit einem sequenziellen Thread verteilt sich nicht allein deshalb auf acht Kerne, weil diese vorhanden sind. Die zusätzlichen Kerne können andere Arbeit ausführen; um dieses Programm zu beschleunigen, muss es jedoch parallele Arbeit enthalten. Auch nach der Aufteilung begrenzt ein verbleibender sequenzieller Anteil den Gewinn.
Wichtige Erkenntnisse
- Parallele Hardware kann die Latenz einer Aufgabe senken oder den Gesamtdurchsatz erhöhen.
- Die dynamische Leistungsaufnahme hängt von der Spannung zum Quadrat und der Frequenz ab.
- ILP, DLP, TLP und Anfrageparallelität erschließen unterschiedliche Arten von Arbeit.
- Nebenläufigkeit kann Zeitaufteilung nutzen; Parallelität führt Operationen gleichzeitig aus.
1.2 Befehls- und Datenströme zählen
Ein Prozessor kann viele Recheneinheiten enthalten, ohne vielen unabhängigen Befehlsströmen zu folgen. Die flynnsche Taxonomie klassifiziert Hardware anhand ihrer Befehls- und Datenströme. Daher wird zunächst festgelegt, welche Ebene des Rechners beschrieben wird.
Die vier Hardwareklassen
| Klasse | Befehlsströme | Datenströme | Bedeutung |
|---|---|---|---|
| SISD | Einer | Einer | Ein Prozessor folgt einem gewöhnlichen Befehlsstrom. |
| SIMD | Einer | Mehrere | Ein Befehl wendet eine Operation auf mehrere Datenelemente an. |
| MISD | Mehrere | Einer | Verschiedene Befehlsströme bearbeiten einen Datenstrom. |
| MIMD | Mehrere | Mehrere | Prozessoren holen unabhängig voneinander Befehle und bearbeiten Daten. |
Ein herkömmlicher Einkernprozessor ist SISD, auch wenn seine Pipeline mehrere Befehle überlappt. Superskalare Befehlsausgabe erzeugt für sich genommen weder unabhängige Befehlszähler noch macht sie den Kern zu MIMD-Hardware.
Ein SIMD-Befehl könnte acht Paare von 32-Bit-Gleitkommazahlen in einem 256-Bit-Register addieren. Er besitzt acht Lanes für die Berechnung, die aber einem Befehl folgen. Zu den SIMD-Erweiterungen mit fester Breite gehören x86 SSE und AVX sowie Arm Neon; auch Vektorarchitekturen fallen in die SIMD-Klasse. Regelmäßige Arrayberechnungen eignen sich gut, weil viele Elemente dieselbe Operation benötigen.
MIMD-Prozessoren wählen ihre Befehle unabhängig voneinander. Mehrkernchips, Multiprozessoren mit gemeinsamem Speicher und Cluster gehören zu dieser Klasse. Die Kerne können unterschiedliche Programme ausführen oder Kopien desselben Programms, die zu unterschiedlichen Zeiten verschiedene Verzweigungen erreichen.
MISD vervollständigt die 2 × 2-Klassifikation. Es gibt wenige eindeutige praktische Beispiele. Manche fehlertoleranten Systeme und Verarbeitungspipelines werden so beschrieben, ihre Klassifikation hängt aber davon ab, wie ihre Ströme definiert werden.
Ein Programm kann dennoch viele Befehlsströme haben
SPMD, ein Programm für mehrere Datenbereiche, ist ein Programmierstil. Jede Arbeitseinheit führt eine Kopie desselben Programms aus und wählt anhand ihrer ID ihren Datenanteil. Die Arbeitseinheiten besitzen unabhängige Ausführungszustände, sodass SPMD üblicherweise auf MIMD-Hardware läuft.
Wenn vier Kerne 4.000 Werte summieren, bearbeitet der Kern mit der ID die Indizes bis . Kern 0 bearbeitet 0 bis 999; Kern 3 bearbeitet 3000 bis 3999. Jeder erzeugt eine Teilsumme, und diese vier Summen werden zusammengeführt. Der gemeinsame Programmtext verlangt nicht, dass alle vier Kerne im selben Taktzyklus denselben Befehl ausgeben.
Jede Ebene gesondert klassifizieren
Betrachte eine CPU mit acht Kernen, deren Threads AVX-Befehle nutzen. Über die Kerne hinweg ist sie MIMD: Die Kerne schreiten unabhängig voneinander voran. Innerhalb einer AVX-Operation ist sie SIMD: Ein Befehl steuert mehrere Lanes. Würde man alle Recheneinheiten zusammenzählen, bliebe diese Struktur verborgen.
Ebenso ist ein Cluster, auf dem Kopien eines wissenschaftlichen Programms laufen, MIMD-Hardware mit SPMD-Software. Seine Knoten kommunizieren über Nachrichten. Eine GPU kombiniert viele unabhängig eingeplante Threadgruppen mit einer SIMD-ähnlichen Ausführung innerhalb jeder Gruppe; NVIDIA nennt diese threadbasierte Ausführungsart SIMT.
Um eine Beschreibung zu klassifizieren, frag dich, wer den nächsten Befehl auswählt. Frag dann, ob dieser Befehl ein Datenelement oder mehrere verarbeitet. Die Anzahl der Quelldateien, installierten Programme oder Recheneinheiten allein kann diese beiden Fragen nicht beantworten.
Wichtige Erkenntnisse
- Die flynnsche Taxonomie zählt Befehlsströme und Datenströme.
- Pipelining innerhalb eines Befehlsstroms bleibt SISD.
- Bei SIMD teilen sich Lanes die Befehlssteuerung; MIMD besitzt unabhängige Befehlsströme.
- SPMD ist ein Programmierstil, der üblicherweise auf MIMD-Hardware läuft.
- Ein Rechner kann über seine Kerne hinweg MIMD und innerhalb jedes Kerns SIMD sein.
1.3 Freie Taktzyklen mit einem anderen Thread nutzen
Ein Cache-Fehlzugriff kann einen Kern Hunderte Taktzyklen warten lassen. Hardware-Multithreading hält die Ausführungszustände mehrerer Threads in einem Kern vor, sodass während dieser Wartezeit nutzbare Befehle eines anderen Threads ausgeführt werden können.
Was wird vervielfacht und was gemeinsam genutzt?
Jeder Hardwarethread besitzt einen eigenen Befehlszähler und Registerzustand. Die Threads teilen sich die Ausführungseinheiten und Caches des Kerns sowie den Zugang zur Speicherbandbreite. Da die Zustände in der Hardware vorliegen, ist ein Wechsel viel schneller als das Sichern und Wiederherstellen eines Betriebssystemprozesses, was Tausende Taktzyklen kosten kann. Die Hardware kann in einem Taktzyklus oder weniger wechseln.
Das Betriebssystem sieht jeden Hardwarethread als logischen Prozessor. Acht physische Kerne mit zwei Hardwarethreads pro Kern stellen 16 logische Prozessoren bereit. Diese Zahl beschreibt verfügbare Threadkontexte und keine 16 vollständigen physischen Kerne.
Drei Planungsverfahren
| Verfahren | Wann wählt der Kern einen anderen Thread? | Wichtigster Zielkonflikt |
|---|---|---|
| Feingranular | In jedem Taktzyklus; wartende Threads werden übersprungen | Verdeckt kurze und lange Wartezeiten, aber jeder Thread wartet zwischen seinen Einsätzen. |
| Grobgranular | Bei einer langen Wartezeit, etwa einem Fehlzugriff im Cache der letzten Ebene | Lässt einen Thread lange laufen, aber Wechsel und erneutes Füllen der Pipeline kosten Zeit. |
| Simultanes Multithreading (SMT) | Mehrere Threads können im selben Taktzyklus Befehle ausgeben | Füllt ungenutzte Ausgabeplätze und teilt dabei die Ressourcen des Kerns. |
Die feingranulare Planung kann reihum zwischen bereiten Threads wechseln, selbst wenn der vorherige Thread fortfahren könnte. Die grobgranulare Planung führt diesen Thread weiter aus, bis eine kostspielige Wartezeit einen Wechsel rechtfertigt. SMT nutzt einen Kern mit Mehrfachausgabe und dynamischer Befehlsplanung: Befehle verschiedener Threads können in einem Taktzyklus unterschiedliche freie Ausgabeplätze belegen. Registerumbenennung hält ihre Zustände getrennt. Intels Hyper-Threading ist eine SMT-Implementierung; einige IBM-POWER-Prozessoren unterstützen acht Hardwarethreads pro Kern.
Latenzverdeckung durchrechnen
Angenommen, ein Thread rechnet 20 Taktzyklen lang und wartet dann 200 Taktzyklen auf den Speicher. Sein Zyklus dauert 220 Taktzyklen. Allein belegt seine nutzbare Arbeit , also 9,1 % der Zeit.
Mit vier Threads, frei versetzter Arbeit und ohne Wechselkosten oder zusätzliche Konkurrenz kann nutzbare Arbeit , also 36,4 % der Zeit belegen. Der Kern hat weiterhin Lücken, weil vier Rechenphasen von jeweils 20 Taktzyklen die Wartezeiten nicht abdecken können.
Elf Threads liefern insgesamt 220 Taktzyklen Rechenarbeit. Während einer 200 Taktzyklen wartet, können die anderen zehn jeweils 20 Taktzyklen rechnen. Dieser ideale Ablauf hält den Kern ausgelastet. Die tatsächliche Rechenleistung hängt auch davon ab, ob die Threads um Speicher und Ausführungsressourcen konkurrieren.
Durchsatz und Abschlusszeit eines Threads
Multithreading erhöht vor allem den Durchsatz. Ein bestimmter Thread kann länger brauchen, weil andere Threads Taktzyklen und Cacheplatz nutzen, die er sonst erhalten würde. Zwei Hardwarethreads, die sich eine Recheneinheit teilen, können nicht beide die Kapazität eines zweiten vollständigen Kerns hinzugewinnen.
Wenn beide Threads bereits dieselben vollständig ausgelasteten Ausführungseinheiten beanspruchen, hat SMT wenig freie Kapazität zu füllen. Wartet einer, während der andere ausführen kann, ist die gemeinsame Nutzung hilfreich. GPUs verdecken Latenz in größerem Maßstab, indem sie für ihre Scheduler viele Threadgruppen bereithalten.
Wichtige Erkenntnisse
- Hardwarethreads vervielfachen den Ausführungszustand und teilen sich zugleich die Ressourcen eines Kerns.
- Feingranularer Wechsel erfolgt in jedem Taktzyklus; grobgranularer Wechsel reagiert auf lange Wartezeiten.
- SMT kann in einem Taktzyklus Befehle verschiedener Threads ausgeben.
- Die Anzahl logischer Prozessoren umfasst Hardwarethreadkontexte.
- Latenzverdeckung benötigt bereite Arbeit und kann durch Konkurrenz um Ressourcen begrenzt werden.
- Ein höherer Durchsatz muss die Laufzeit eines einzelnen Threads nicht verkürzen.
2Den nutzbaren parallelen Speedup abschätzen
Zusätzliche Prozessoren teilen nur die Arbeit auf, die parallel ausgeführt werden kann, und verursachen eigene Kosten. Beginne mit der Gesamtlaufzeit und dem sequenziellen Anteil und unterscheide dann, ob eine feste Aufgabe schneller abgeschlossen oder eine größere Aufgabe in derselben Zeit gelöst wird. Lastverteilung und Koordination erklären weitere Verluste. Das Roofline-Modell ergänzt eine andere Grenze: ob der Speicher die verfügbaren Recheneinheiten schnell genug versorgen kann.
2.1 Gewinn und Obergrenze berechnen
Ein paralleler Rechner ist nützlich, wenn er die Gesamtlaufzeit verändert. Der Speedup vergleicht den besten sequenziellen Lauf mit dem parallelen Lauf; die Effizienz setzt diesen Gewinn ins Verhältnis zur Anzahl der verwendeten Prozessoren.
Mit der gemessenen Zeit beginnen
Für Prozessoren gilt und . Eine sequenzielle Aufgabe von 100 s, die auf acht Kernen in 16 s abgeschlossen wird, hat den Speedup . Ihre Effizienz beträgt , also etwa 78 %. Miss für beide Läufe die Gesamtlaufzeit und verwende dieselbe feste Aufgabe.
Zeit in verbesserten und unveränderten Anteil aufteilen
Im amdahlschen Gesetz ist der Anteil der ursprünglichen Ausführungszeit, den eine Verbesserung beschleunigt. Es ist ein Zeitanteil und kein Anteil der Quellcodezeilen. Wird dieser Teil -mal so schnell, gilt
Der unveränderte Teil benötigt weiterhin seine volle ursprüngliche Zeit. Für eine ideale parallele Aufteilung auf Prozessoren setze . Dieses Modell schließt den Aufwand für Kommunikation, Lastungleichgewicht und Synchronisation aus; diese Kosten können die gemessene Zeit verlängern.
Bei 90 % paralleler Zeit und zehn Prozessoren beträgt die normierte Zeit . Der Speedup ist und die Effizienz etwa 53 %. Bei 100 Prozessoren beträgt die Zeit und der Speedup etwa 9,17. Eine Verzehnfachung der Prozessorzahl hat den Speedup nicht verdoppelt.
Die Grenze vor dem Kauf weiterer Prozessoren bestimmen
Mit wachsender Prozessorzahl nähert sich die parallele Zeit der Null. Der sequenzielle Anteil bleibt bestehen und ergibt die Obergrenze . Ein sequenzieller Anteil von 10 % begrenzt den idealen Speedup auf 10; ein Anteil von 5 % begrenzt ihn auf 20. Endliche Prozessorzahlen nähern sich diesen Obergrenzen von unten.
Das amdahlsche Gesetz bewertet auch eine einzelne schnellere Befehlsklasse. Wenn die Multiplikation 30 % eines Laufs beansprucht und viermal so schnell wird, beträgt die normierte Zeit . Das gesamte Programm ist nur -mal so schnell.
Von einem Zielwert rückwärts rechnen
Für einen Speedup von 90 auf 100 Prozessoren schreibe . Daher gilt . Nur etwa 0,11 % der ursprünglichen Zeit dürfen sequenziell bleiben. Ein Ziel nahe der Prozessorzahl verlangt einen sehr kleinen unveränderten Anteil.
Ein gemessener Speedup kann manchmal die Prozessorzahl übersteigen. Durch Aufteilung einer großen Arbeitsmenge passt möglicherweise jeder Teil in den Cache, sodass die Kosten pro Operation sinken. Dieser superlineare Speedup verändert eine Voraussetzung des einfachen Modells: Die parallele Arbeit ist nicht mehr bloß die ursprüngliche Arbeit, die in gleiche Anteile geteilt wird. Prüfe Algorithmus, Datengröße und Cacheverhalten, bevor du eine solche Messung deutest.
Wichtige Erkenntnisse
- Der Speedup ist die sequenzielle Zeit geteilt durch die parallele Zeit; die Effizienz ist der Speedup geteilt durch die Prozessorzahl.
- Amdahls Anteil bezieht sich auf die ursprüngliche Ausführungszeit.
- Nur der verbesserte Anteil wird durch den Beschleunigungsfaktor geteilt.
- Der sequenzielle Anteil legt die Speedup-Obergrenze bei fester Arbeitslast fest.
- Cacheeffekte können die Kosten der Arbeit verändern und einen gemessenen superlinearen Speedup erzeugen.
2.2 Die Problemgröße ändern und Kosten einbeziehen
Zusätzliche Prozessoren können entweder dieselbe Aufgabe verkürzen oder einen Rechner eine größere Aufgabe bearbeiten lassen. Starke Skalierung und schwache Skalierung messen diese unterschiedlichen Einsatzarten. Beide hängen davon ab, wie viel nutzbare Arbeit nach Abzug der Koordinationskosten übrig bleibt.
Feste und wachsende Arbeit
Bei starker Skalierung bleibt die Problemgröße fest. Bei schwacher Skalierung wächst die Arbeit proportional zur Prozessorzahl, sodass die Arbeit pro Prozessor konstant bleibt. Perfekte schwache Skalierung hält die Gesamtlaufzeit konstant, während die Gesamtarbeit wächst.
Betrachte zehn sequenzielle Additionen, gefolgt von der elementweisen Summe zweier Matrizen. Eine 10 × 10-Matrix benötigt 100 parallele Additionen. Die Gesamtzeit beträgt 110 Einheiten auf einem Prozessor, 20 auf zehn und 12 auf fünfzig: Die Speedups betragen 5,5 und etwa 9,2. Eine 40 × 40-Matrix benötigt 1.600 parallele Additionen. Die Zeiten betragen 1.610, 170 und 42 Einheiten: Die Speedups betragen etwa 9,5 und 38,3. Beim größeren Problem fällt die nutzbare parallele Arbeit gegenüber denselben zehn sequenziellen Additionen stärker ins Gewicht.
Gustafsons anderer Ausgangswert
Das gustafsonsche Gesetz nutzt den sequenziellen Zeitanteil, der beim parallelen Lauf gemessen wird. Bezeichnet man diesen Anteil mit , beträgt der skalierte Speedup . Für und 100 Prozessoren gilt .
Das amdahlsche Gesetz ergibt bei 5 % sequenzieller Zeit im ursprünglichen Lauf auf einem Prozessor nur etwa 16,8 auf 100 Prozessoren. Diese Anteile haben unterschiedliche Bezugsgrößen und beantworten unterschiedliche Fragen. Ein skalierter Speedup von 95,05 bedeutet nicht, dass die ursprüngliche Aufgabe mit fester Größe 95,05-mal so schnell abgeschlossen wurde.
Die langsamste Arbeitseinheit bestimmt den Endzeitpunkt
Werden 400 gleich große Arbeitseinheiten auf acht Kerne verteilt, beträgt der ausgeglichene Anteil je 50. Angenommen, einer erhält 120, während die anderen sieben je 40 erhalten. Der Schritt dauert 120 Zeiteinheiten, sodass der Speedup beträgt. Wer durch die durchschnittliche Last dividiert, übersieht das Warten auf den am stärksten belasteten Kern.
Auch das Erzeugen und Einplanen von Threads, Nachrichten, Barrieren und Konkurrenz um gemeinsame Bandbreite verbrauchen Zeit. Granularität ist der Umfang der Berechnungen zwischen Kommunikations- oder Synchronisationsereignissen. Größere Teilstücke senken die Koordinationskosten pro nützlicher Operation, aber bei zu wenigen Teilstücken können einige Arbeitseinheiten untätig bleiben.
Mehr Prozessoren können die Zeit verlängern
Für eine vollständig parallelisierbare Aufgabe von 100 s mit einem Aufwand von Sekunden gilt . Zehn Prozessoren benötigen 15 s. Vierzehn benötigen etwa 14,14 s, nahe dem Minimum bei ungefähr Prozessoren. Vierzig benötigen 22,5 s, weil ihr Aufwand von 20 s die kürzere nutzbare Berechnung überwiegt.
Gib bei der Ergebnisdarstellung an, ob die Arbeit fest blieb oder wuchs, und vergleiche mit der Zeit des besten sequenziellen Programms. Wenn du eine langsame parallele Implementierung auf einem Prozessor als Ausgangswert nutzt, kann der angegebene Speedup überhöht sein.
Wichtige Erkenntnisse
- Starke Skalierung hält die Gesamtarbeit fest; schwache Skalierung hält die Arbeit pro Prozessor fest.
- Gustafsons sequenzieller Anteil wird beim parallelen Lauf gemessen.
- Die langsamste Arbeitseinheit bestimmt den Endzeitpunkt eines parallelen Schritts.
- Kommunikation, Synchronisation und Ressourcenkonkurrenz können mit der Prozessorzahl zunehmen.
- Bei der Granularität stehen Koordinationskosten und Lastverteilung in einem Zielkonflikt.
- Verwende bei der Angabe des Speedups die Gesamtlaufzeit und das beste sequenzielle Programm als Vergleichsbasis.
2.3 Das Rechenleistungs- oder Bandbreitendach bestimmen
Die beworbene Rechenrate eines Prozessors ist nur eine Grenze. Das Roofline-Modell fragt auch, ob der Speicher Daten schnell genug liefern kann, um diese Rate aufrechtzuerhalten. Es gibt eine obere Schranke an und hilft damit, eine sinnvolle Richtung für Optimierungen zu finden.
Operationen pro DRAM-Byte zählen
Die arithmetische Intensität ist die Anzahl der Gleitkommaoperationen geteilt durch die zwischen DRAM und Prozessor übertragenen Byte. Die ursprüngliche Arbeit zum Roofline-Modell nennt dieses DRAM-bezogene Maß operationale Intensität. Zähle den Datenverkehr an dieser Schnittstelle einschließlich der Lese- und Schreibzugriffe.
DAXPY berechnet mit Zahlen doppelter Genauigkeit. Jedes Element benötigt eine Multiplikation und eine Addition: zwei Gleitkommaoperationen. Das Lesen eines 8 Byte großen Werts aus jedem Array und das Zurückschreiben von 8 Byte übertragen 24 Byte. Die Intensität beträgt Gleitkommaoperationen pro Byte. Der skalare Koeffizient wird wiederverwendet; in diesem Verkehrsmodell wird er nicht für jedes Element erneut aus dem DRAM geladen.
Beide Grenzen anwenden
Sind die Spitzenrechenleistung und die maximale Speicherbandbreite , dann ist das Dach . Bandbreite mal Gleitkommaoperationen pro Byte ergibt die Einheit Gleitkommaoperationen pro Sekunde. In einem doppelt logarithmischen Diagramm steigt die Bandbreitengrenze an und die Rechenleistungsgrenze verläuft waagerecht.
Bei 1.000 GFLOP/s und 100 GB/s liegt der Knickpunkt bei Gleitkommaoperationen pro Byte. Unterhalb dieser Intensität liegt das Bandbreitendach niedriger, darüber das Rechenleistungsdach. DAXPY ist durch GFLOP/s begrenzt, also weniger als 1 % der Spitzenrechenleistung des Rechners. Ein Kernel mit der Intensität 2 ist auf 200 GFLOP/s begrenzt.
Ein speichergebundener Kernel kann dennoch von zusätzlichen Kernen oder breiteren SIMD-Einheiten profitieren, solange zu wenige Anfragen gleichzeitig bearbeitet werden, um die verfügbare Bandbreite zu nutzen. Sobald die Bandbreite ausgeschöpft ist, hebt zusätzliche Rechenkapazität sein Dach nicht an. Eine höhere Bandbreite oder mehr nutzbare Berechnung pro übertragenem Byte hebt es dagegen an.
Wiederverwendung verändert die Intensität
Die Multiplikation zweier 1000 × 1000-Matrizen mit doppelter Genauigkeit führt etwa Gleitkommaoperationen aus. Wenn jedes Element der beiden Eingabematrizen und der Ausgabematrix die DRAM-Grenze einmal überquert, beträgt der gesamte Datenverkehr 24 MB. Das ergibt etwa 83 Gleitkommaoperationen pro Byte und liegt über dem Knickpunkt des Beispielrechners.
Eine naive Implementierung kann Matrixwerte wiederholt aus dem DRAM lesen. Kachelung organisiert Schleifen um Teilmatrizen, die in den Cache passen, sodass Werte für viele Operationen genutzt werden, bevor sie ersetzt werden. Der Algorithmus bleibt eine Matrixmultiplikation, aber die Implementierung überträgt pro Operation weniger Byte aus dem oder in das DRAM.
Ein Dach ist kein Messergebnis
Ein Kernel kann wegen Abhängigkeiten, Befehlsaufwand, ungünstiger Zugriffsmuster oder zu wenig paralleler Arbeit unter der Schranke bleiben. Niedrigere Leistungsgrenzen zeigen solche Einschränkungen: Fehlende SIMD-Befehle können die Rechenleistungsgrenze senken, ungünstige Zugriffsmuster dagegen die nutzbare Bandbreite. Ein Punkt unterhalb eines Dachs muss untersucht werden; das Dach allein sagt seine genaue Rechenrate nicht voraus.
Wichtige Erkenntnisse
- Die Intensität zählt Gleitkommaoperationen pro Byte an der DRAM-Grenze.
- Das Dach ist der kleinere Wert aus Spitzenrechenleistung und Bandbreite mal Intensität.
- Der Knickpunkt ist die Spitzenrechenleistung geteilt durch die maximale Bandbreite.
- Wiederverwendung kann die Intensität erhöhen, ohne den mathematischen Algorithmus zu ändern.
- Ein Roofline-Diagramm gibt eine obere Schranke an; niedrigere Leistungsgrenzen erklären weitere Einschränkungen.
3Arrays mit Lanes und GPU-Threads verarbeiten
Arrays bieten sich für wiederholte Operationen auf unabhängigen Elementen an, die auf Lanes und Threads verteilt werden können. Verfolge diese Idee von SIMD-Registern fester Breite über Vektorschleifen mit variabler Länge bis zu GPU-Warps. Jeder Entwurf benötigt eine wirksame Datenanordnung und genügend Arbeit, um seine Hardware auszulasten. Ein kleiner Kernel zeigt, wie Arrayindizes zu Threadindizes werden; die Übertragungskosten entscheiden mit darüber, ob die Auslagerung der gesamten Aufgabe hilft.
3.1 Arrayoperationen über SIMD-Lanes ausführen
Eine SIMD-Erweiterung teilt ein breites Register in Lanes. Ein Befehl führt in jeder Lane dieselbe Operation aus. Deshalb eignen sich regelmäßige Schleifen über unabhängige Elemente gut dafür.
Lanes und Iterationen zählen
Die Anzahl der Lanes ist die Registerbreite geteilt durch die Elementbreite. Ein 256-Bit-Register fasst acht 32-Bit-Gleitkommazahlen einfacher Genauigkeit, vier 64-Bit-Gleitkommazahlen doppelter Genauigkeit oder 32 Elemente zu je einem Byte. SSE verwendet 128-Bit-XMM-Register; AVX und AVX2 verwenden 256-Bit-YMM-Register. Im 64-Bit-Modus gibt es 16 YMM-Register. AVX-512 stellt 32 512-Bit-ZMM-Register bereit, die jeweils 16 Gleitkommazahlen einfacher Genauigkeit fassen. Arm Neon verwendet 128-Bit-Register.
Für c[i] = a[i] + b[i] über 1.000 Gleitkommazahlen verarbeiten Befehle mit acht Lanes das Array in 125 Vektoriterationen. Jede Iteration lädt zwei Vektoren, addiert deren Lanes und speichert einen Vektor. Bei 1.003 Elementen bleiben nach 125 vollständigen Iterationen drei Elemente übrig. Eine skalare Restschleife oder eine maskierte Operation verarbeitet diesen kurzen Rest, ohne über das Ende der Arrays hinaus zuzugreifen.
Acht Lanes beschreiben eine Rechenkapazität und garantieren keinen achtfachen Speedup des Programms. Initialisierung, Restverarbeitung, sequenzielle Arbeit und Speicherbandbreite beanspruchen weiterhin Zeit.
Eine Anordnung wählen, die ein Register füllt
Einfache Vektorladebefehle eignen sich gut für zusammenhängende Elemente mit Schrittweite 1. Ein Array aus {x, y, z}-Punkten mit 4 Byte pro Koordinate legt aufeinanderfolgende x-Felder im Abstand von 12 Byte ab. Eine Struktur von Arrays legt die x-Werte zusammen ab, danach die y-Werte und dann die z-Werte. Werden nur die x-Werte verarbeitet, kann nun ein zusammenhängender Ladezugriff einen Vektor füllen. Gather- und Scatter-Befehle unterstützen verstreute Adressen, verursachen aber meist höhere Zugriffskosten.
Bedingte Arbeit innerhalb der Lanes ausführen
Für c[i] = (a[i] > 0) ? a[i] : 0 erzeugt ein Vergleich eine Maske mit einem Wert pro Lane. Ein Auswahlbefehl wählt zwischen möglichen Ergebnissen, oder maskierte Befehle aktualisieren nur die ausgewählten Lanes. Inaktive Lanes behalten oder erhalten Werte entsprechend dem Maskenverhalten des Befehls.
Wenn unterschiedliche Lanes lange, unterschiedliche Verzweigungen benötigen, müssen die Befehle dieser Pfade weiterhin ausgegeben werden, wobei nur die jeweils betroffenen Lanes aktiv sind. Die Gruppe kann nicht jeder Lane einen eigenen Befehlsstrom geben. Eine Maske erhält die Korrektheit, lässt aber nicht alle Befehle dieser Pfade verschwinden.
Unabhängigkeit der Iterationen prüfen
Compiler können Schleifen automatisch vektorisieren; Programmierer können auch Bibliotheken oder Intrinsics wie _mm256_add_ps verwenden. Der Compiler muss nachweisen, dass gleichzeitige Iterationen das Ergebnis bewahren. Die Rekurrenz a[i] = a[i-1] + b[i] hängt vom neuen Wert der vorhergehenden Iteration ab und kann nicht direkt als unabhängige Elemente vektorisiert werden. Eine mögliche Überlappung von Zeigern, Aliasing genannt, kann ebenfalls eine Überlappungsprüfung zur Laufzeit nötig machen oder die Vektorisierung verhindern.
Eine Reduktion hält mehrere Teilsummen in einem Register und führt anschließend die Lanes zusammen. Das Umordnen von Gleitkommaadditionen kann die letzten Bits verändern, weil die Addition nicht assoziativ ist. Ganzzahlige Summen stimmen überein, wenn bei der gewählten Arithmetik kein Überlauf auftritt oder definierte modulare Arithmetik verwendet wird.
SIMD und Mehrkernausführung lassen sich kombinieren. Acht Kerne, die jeweils eine Addition mit acht Lanes pro Taktzyklus ausgeben, können bis zu 64 Gleitkommaadditionen pro Taktzyklus ausführen. Sie unterliegen weiterhin gemeinsamen Grenzen wie der Speicherbandbreite.
Wichtige Erkenntnisse
- Die Anzahl der SIMD-Lanes hängt sowohl von der Registerbreite als auch von der Elementbreite ab.
- Reste benötigen eine skalare Restschleife oder einen korrekt maskierten Zugriff.
- Zusammenhängende Anordnungen helfen Vektorladebefehlen, ihre Lanes effizient zu nutzen.
- Masken behandeln Bedingungen pro Element bei gemeinsamer Befehlsausgabe.
- Abhängigkeiten und Aliasing können eine direkte Vektorisierung verhindern.
- Bei Gleitkommareduktionen kann eine Umgruppierung der Operationen die Rundung verändern.
3.2 Jeden Schleifendurchlauf durch die Vektorlänge steuern
Eine Vektorarchitektur beschreibt eine Operation auf einer Folge von Elementen und speichert die aktive Vektorlänge in einem Register. Dasselbe Programm kann auf Implementierungen mit unterschiedlich großen Vektorregistern laufen.
Kapazität, Länge und Lanes unterscheiden
Die RISC-V-Vektorerweiterung RVV wurde 2021 in Version 1.0 ratifiziert. Sie stellt 32 Vektorregister bereit, von v0 bis v31. Jede Implementierung wählt ihre Registerbreite VLEN als Zweierpotenz. Elementbreite und Registergruppierung bestimmen, wie viele Elemente hineinpassen.
Die aktive Länge vl ist die Anzahl der Elemente, die die nächsten Vektorbefehle bearbeiten. vsetvli konfiguriert die Vektoroperation anhand der verbleibenden Elementzahl und des gewünschten Elementformats und gibt die gewählte Länge vl zurück. Der Code rückt um diesen Rückgabewert weiter. Das ist wichtig, weil portabler Code die tatsächlich von der Implementierung gewählte Länge verwenden muss.
Lanes sind die parallelen Pipelines, die die Vektoroperation ausführen. Eine Addition mit 32 Elementen auf vier Lanes benötigt nach dem Anlaufen der Pipeline etwa acht Taktzyklen. Ein Vektor mit 32 Elementen benötigt keine 32 Recheneinheiten. Längere Vektoren und mehr Lanes sind voneinander unabhängige Entwurfsentscheidungen.
Eine Schleife mit Strip-Mining aufteilen
Strip-Mining teilt eine lange Schleife in vektorgroße Stücke. Für 100 Elemente und höchstens 32 pro Operation ist 32, 32, 32 und 4 eine mögliche Folge von Längen. Jeder Durchlauf lädt den nächsten Teil, berechnet und speichert ihn und verringert die verbleibende Anzahl um den Wert von vl.
DAXPY über 1.000 Gleitkommazahlen doppelter Genauigkeit kann entsprechend 31 Durchläufe mit je 32 und einen mit acht Elementen verwenden, insgesamt also 32 Durchläufe. RVV kann gegen Ende andere zulässige Längen wählen. Deshalb verwendet die Schleife den zurückgegebenen Wert von vl, statt eine bestimmte letzte Aufteilung anzunehmen. Das Setzen der aktiven Länge behandelt den kurzen Rest ohne separate skalare Restschleife.
Das Muster zur Schleifensteuerung lautet:
- Fordere eine Länge für die verbleibenden Elemente an.
- Lade und berechne diese Anzahl von Elementen.
- Speichere die Ergebnisse.
- Setze die Zeiger weiter und ziehe die zurückgegebene Länge ab.
Ein Vektorbefehl beschreibt viele Elementoperationen. Gegenüber einem skalaren Rumpf, der für jedes Element wiederholt wird, verringert das den Aufwand für das Holen und Dekodieren von Befehlen.
Abhängige Operationen und nicht zusammenhängende Daten versorgen
Verkettung erlaubt einem verarbeitenden Vektorbefehl, mit frühen Ergebnissen zu beginnen, bevor der erzeugende Befehl spätere Elemente fertiggestellt hat. Bei einer Multiplikation mit anschließender Addition kann das erste Produkt in die Additionspipeline gelangen, während spätere Produkte noch gebildet werden.
Eine Maske, bei RVV häufig im Register v0, wählt aus, welche Elemente aktualisiert werden. Ein Ladebefehl mit fester Schrittweite greift mit einem festen Byteabstand zu. In einer zeilenweise gespeicherten 100 × 100-Matrix aus Gleitkommazahlen doppelter Genauigkeit führt ein Schritt nach unten in einer Spalte um 100 Elemente oder 800 Byte weiter. Indizierte Lade- und Speicherbefehle verwenden explizite Adressen für unregelmäßige Daten.
Sowohl Vektorarchitekturen als auch Erweiterungen fester Breite gehören in der flynnschen Taxonomie zu SIMD. Erweiterungen fester Breite kodieren bestimmte Breiten; RVV-Code beschreibt Längen, die sich an die verfügbare Hardware anpassen. Beide benötigen weiterhin eine Ausführungsreihenfolge, die die Datenabhängigkeiten der Schleife beachtet.
Wichtige Erkenntnisse
- Die Vektorlänge gibt an, wie viele Elemente eine Operation bearbeitet; die Lanes geben an, wie viele gleichzeitig voranschreiten können.
- RVV-Code rückt um die tatsächlich von
vsetvlizurückgegebene Länge weiter. - Strip-Mining verarbeitet ein langes Array in vektorgroßen Stücken.
- Verkettung überlappt abhängige Vektoroperationen Element für Element.
- Masken, Schrittweiten und indizierte Zugriffe unterstützen weniger regelmäßige Daten.
3.3 Eine GPU mit vielen Warps auslasten
Eine CPU konzentriert ihre Ressourcen auf geringe Latenz für wenige Threads. Eine GPU konzentriert ihre Ressourcen auf hohen Durchsatz über viele datenparallele Threads. Bei der Wahl zwischen beiden muss die gesamte Aufgabe einschließlich der Datenübertragung betrachtet werden.
Threads laufen auf Streaming Multiprocessors
Eine GPU von NVIDIA enthält viele Streaming Multiprocessors, kurz SMs. Ein SM besitzt Rechen-Lanes, Warp-Scheduler, einen großen Registersatz und schnellen Shared Memory auf dem Chip. Ein „CUDA Core“ bezeichnet eine Rechen-Lane und keinen vollständigen unabhängigen Kern wie bei einer CPU. AMD nennt die entsprechende Verarbeitungsgruppe Compute Unit.
NVIDIA fasst Threads zu Warps aus jeweils 32 Threads zusammen. Ein ausgegebener Befehl wirkt auf die aktiven Threads des Warps, die jeweils eigene Registerwerte und Daten besitzen. Das ist SIMT: ein einzelner Befehl für mehrere Threads. AMD verwendet Wavefronts mit je nach Architektur 32 oder 64 Threads.
Ein SM hält viele Warps vor. Wartet einer auf den Speicher, wählt der Scheduler einen anderen bereiten Warp. Mehr bereite Threads als Lanes verdecken Latenz; sie verringern nicht die Latenz eines einzelnen DRAM-Zugriffs.
Divergenz lässt Lanes untätig
Nehmen Threads eines Warps unterschiedliche Verzweigungen, führt der Warp die benötigten Pfade aus und maskiert dabei die jeweils anderen Threads. Eine gleichmäßig aufgeteilte if-else-Verzweigung mit gleich aufwendigen Zweigen kann ungefähr die doppelte Zeit eines Pfads benötigen, weil beide Pfade ausgeführt werden müssen. Eine Verzweigung kann unproblematisch sein, wenn ihre Bedingung innerhalb jedes Warps einheitlich ist, selbst wenn verschiedene Warps unterschiedliche Pfade wählen.
Speicherzugriffe aufeinander abstimmen
Register pro Thread halten private Werte. Shared Memory pro Block ist ein vom Programm verwalteter Speicher auf dem Chip. L1- und L2-Caches ermöglichen automatische Wiederverwendung, während der globale Speicher das DRAM der GPU ist, etwa GDDR oder HBM.
Wenn 32 Threads aufeinanderfolgende Wörter zu je 4 Byte lesen, kann ihr Bereich von 128 Byte durch wenige breite Transaktionen bedient werden. Dieses Coalescing nutzt die Bandbreite effizient. Verstreute Adressen können für dieselben nutzbaren Daten viele Transaktionen benötigen. Die Wiederverwendung im Shared Memory kann wiederholten Verkehr zum globalen Speicher weiter verringern.
Die Übertragungszeit einbeziehen
Bei einer separaten GPU werden Eingaben und Ergebnisse über eine Verbindung wie PCIe oder NVLink übertragen. Das Senden von 800 MB und Empfangen von 400 MB überträgt bei effektiv 20 GB/s insgesamt 1,2 GB in 0,06 s, also 60 ms. Mit einem Kernel von 10 ms ergeben sich insgesamt 70 ms. Eine CPU, die in 50 ms fertig ist, gewinnt diesen Vergleich trotz der viel schnelleren GPU-Berechnung.
Bleiben Daten über mehrere Kernel hinweg auf der GPU, entfallen wiederholte Übertragungen. Große, regelmäßige Aufgaben können außerdem genügend nutzbare Rechenarbeit bieten, um die Kosten für Übertragung und Start aufzuwiegen. Bei kleinen, sequenziellen Aufgaben mit vielen Verzweigungen ist oft eine CPU im Vorteil.
Bei geeigneter Arbeit weiter spezialisieren
Ein heterogenes System verwendet die CPU für die Ausführung von Programm und Betriebssystem und einen Beschleuniger für geeignete Teile. Domänenspezifische Beschleuniger wie TPUs und NPUs widmen ihre Hardware einer enger gefassten Aufgabe, häufig der Matrixmultiplikation mit 8-Bit-Ganzzahlen oder 16-Bit-Gleitkommazahlen. Spezialisierung kann in diesem Bereich die Rechenleistung pro Watt verbessern und verringert zugleich die Bandbreite der Aufgaben, die die Hardware gut bewältigt.
Wichtige Erkenntnisse
- Ein SM plant Warps ein; ein CUDA Core ist eine Rechen-Lane.
- NVIDIA-Warps enthalten 32 Threads mit getrennten Daten und gemeinsamer Befehlsausgabe.
- Bereite, vorgehaltene Warps verdecken Speicherwartezeiten.
- Divergenz und verstreute Zugriffe verschwenden Lane-Zeit und Speicherbandbreite.
- Bei Auslagerungsentscheidungen zählen Übertragungen ebenso wie die Kernelzeit.
- Spezialisierte Beschleuniger tauschen Flexibilität gegen Effizienz in einem engeren Bereich.
3.4 Arrayelemente GPU-Threads zuordnen
Ein GPU-Kernel läuft einmal pro gestartetem Thread. Der Host startet ein Grid aus Threadblöcken, und jeder Thread berechnet, welchen Teil der Daten er bearbeitet. CUDA nennt die CPU den Host und die GPU das Gerät; OpenCL verwendet die verwandten Begriffe Work-Item und Work-Group.
Einen eindeutigen Index berechnen
Bei einem eindimensionalen Start lautet der Elementindex blockIdx.x * blockDim.x + threadIdx.x. Bei 256 Threads pro Block bearbeitet Thread 5 in Block 2 das Element 517: Die beiden vorhergehenden Blöcke umfassen 512 Elemente, und der Thread ergänzt einen Versatz von fünf. Thread- und Blockindizes beginnen bei null.
Ein SAXPY-Kernel wendet eine Multiplikation und eine Addition auf jede Gleitkommazahl an:
__global__ void saxpy(int n, float a, float *x, float *y) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
y[i] = a * x[i] + y[i];
}
}
Das Grid ersetzt die gewöhnliche Schleife über die Elemente. Jeder aktive Thread aktualisiert sein eigenes y[i]; benachbarte Threads verwenden benachbarte Adressen und unterstützen so Coalescing.
Die Blockzahl aufrunden
Für 1.000.000 Elemente und 256 Threads pro Block benötigt das Grid Blöcke. Diese starten 1.000.192 Threads. Die letzten 192 haben Indizes außerhalb des Arrays, sodass die Bedingung i < n ihre Speicherzugriffe verhindert. Abrunden würde zu wenige Threads starten und gültige Elemente unbearbeitet lassen.
Ein Block mit 256 Threads bildet acht Warps. Blöcke können beliebigen SMs zugewiesen und in beliebiger Reihenfolge ausgeführt werden. Die Korrektheit des Programms darf nicht davon abhängen, dass Block 0 vor Block 1 fertig wird.
Arbeit von Host und Gerät organisieren
Ein typischer Host reserviert Gerätespeicher, kopiert die Eingaben, startet saxpy<<<3907, 256>>>(n, 2.0f, d_x, d_y) und erhält die Ergebnisse. Der Start ist asynchron: Die CPU kann fortfahren, während der Kernel läuft. Der Host muss durch Synchronisation oder eine geeignete blockierende Ergebniskopie warten, bevor er die fertigen Daten verwendet.
Innerhalb eines Blocks zusammenarbeiten
Threads eines Blocks können Werte über Shared Memory austauschen und sich bei __syncthreads() treffen. Jeder Thread, der an dieser blockweiten Barriere teilnimmt, muss sie erreichen. Die Barriere innerhalb von if (i < n) zu platzieren, ist unsicher, wenn nur einige Threads die Bedingung erfüllen.
Für eine Reduktion von 1.024 Werten im Shared Memory addieren 512 Threads Paare, dann führen 256 die Ergebnisse zusammen, danach 128 und so weiter. Zehn Halbierungsstufen lassen eine Summe übrig. Eine Barriere zwischen den Stufen verhindert, dass ein Thread ein Teilergebnis verarbeitet, bevor der erzeugende Thread es schreibt.
Ein gewöhnlicher Start besitzt keine gridweite Barriere. Getrennte Blöcke können Teilsummen schreiben, die ein zweiter Kernel zusammenführt; alternativ können geeignete atomare Operationen ein globales Ergebnis aktualisieren. Eine Blockbarriere allein kann verschiedene Blöcke nicht koordinieren. Eine Schleife mit Grid-Schrittweite ist ein weiteres Zuordnungsmuster: Jeder Thread bearbeitet mehrere Elemente und rückt dabei jeweils um die gesamte Grid-Größe weiter.
Wichtige Erkenntnisse
- Berechne Indizes aus Blockgröße, Blockindex und Threadindex.
- Runde Blockzahlen auf und sichere Zugriffe außerhalb des Bereichs ab.
- Kernelstarts erfolgen asynchron zum Host.
- Shared Memory und
__syncthreads()unterstützen die Zusammenarbeit innerhalb eines Blocks. - Gewöhnliche Blöcke laufen unabhängig; das Zusammenführen ihrer Ergebnisse benötigt einen anderen Mechanismus.
4Speicher gemeinsam nutzen und Cachebesitz verfolgen
Ein gemeinsamer Adressraum ermöglicht es Threads, Werte durch gewöhnliche Speicheroperationen auszutauschen. Private Caches machen diese Operationen schnell, erzeugen aber mehrere Kopien, die koordiniert werden müssen. Verfolge eine Reduktion von lokaler Arbeit bis zu gemeinsamen Ergebnissen und anschließend die Berechtigungen, die Cachekopien kohärent halten. Der Speicherbedarf von Verzeichnissen und False Sharing verbinden diese Korrektheitsmechanismen mit den Leistungskosten der Datenplatzierung.
4.1 Einen Adressraum teilen und Teilergebnisse zusammenführen
Ein Multiprozessor mit gemeinsamem Speicher stellt seinen Prozessoren einen physischen Adressraum zur Verfügung. Threads kommunizieren durch Lade- und Speicherzugriffe auf gemeinsame Variablen und halten zugleich private Werte wie lokale Teilsummen vor.
Gemeinsame Adressen können unterschiedlich viel kosten
Bei UMA, dem gleichförmigen Speicherzugriff, ist die Speicherzugriffszeit von jedem Prozessor aus ungefähr gleich. Bei NUMA, dem nicht gleichförmigen Speicherzugriff, erreicht ein Sockel seinen lokalen Speicher schneller als den Speicher eines anderen Sockels. Der Adressraum wird gemeinsam genutzt, obwohl der physische Speicher verteilt ist.
Bei 70 % lokalen Zugriffen mit 80 ns und 30 % entfernten Zugriffen mit 140 ns beträgt das gewichtete arithmetische Mittel ns. Werden Daten nahe bei ihrem Hauptnutzer abgelegt, sinkt der Anteil entfernter Zugriffe. Würde man jeden Zugriff als lokal zählen, würde man die Zeit unterschätzen.
Eine verbreitete Cacheanordnung gibt jedem Kern private L1-Befehls- und Datencaches, häufig einen privaten L2-Cache und einen gemeinsam genutzten Cache der letzten Ebene. Ein Ring oder Gitter verbindet die Kerne und Caches. Gemeinsamer Speicher beschreibt die Adressierung; er verlangt nicht, dass jeder Kern denselben Cache nutzt.
Aufteilen, berechnen und reduzieren
Um 64.000 Werte auf 64 Kernen zu summieren, bearbeitet jeder Kern 1.000 Werte. Die 64 Teilsummen können in einem Reduktionsbaum zusammengeführt werden. Die Anzahl aktiver Addierer halbiert sich auf jeder Stufe: 32, 16, 8, 4, 2 und 1. Sechs Stufen führen die Teilergebnisse zusammen.
Mit etwa 1.000 lokalen Additionsschritten und sechs Schritten zum Zusammenführen ergeben sich vor Berücksichtigung des Aufwands etwa 1.006 Schritte auf dem längsten Pfad. Der Baum vermeidet, dass jede Eingabe einen gemeinsamen Zähler aktualisiert. Jede Stufe hängt von den fertigen Werten der vorherigen Stufe ab, sodass sich die Arbeitseinheiten zwischen den Stufen an Barrieren treffen.
OpenMP kann dies mit #pragma omp parallel for reduction(+:sum) ausdrücken. Die Anweisung teilt Iterationen auf, gibt Threads private Teilsummen und führt sie zusammen. POSIX-Threads bieten eine explizitere Kontrolle über die Threadverwaltung.
Eine Aktualisierung unteilbar machen
Eine atomare Lese-Änderungs-Schreiboperation macht eine Aktualisierung aus Sicht anderer Prozessoren unteilbar. Atomarer Austausch und Fetch-and-Add sind Beispiele. Ein Laden mit anschließendem gewöhnlichem Speichern sind zwei getrennte Operationen, sodass ein anderer Thread dazwischen eingreifen kann.
RISC-V stellt lr.w und sc.w bereit. Der reservierende Ladebefehl liest ein Wort und legt eine Reservierung an. Der bedingte Speicherbefehl versucht zu schreiben und meldet Erfolg oder Misserfolg. Ein zwischenzeitlicher Schreibzugriff auf die reservierte Stelle lässt den Versuch fehlschlagen; die Software versucht es erneut. Die Reservierung blockiert keine anderen Prozessoren, und ein Fehlschlag kann auch aus anderen zulässigen Gründen auftreten. Diese Grundoperationen unterstützen Synchronisationsmechanismen.
Gemeinsame Engpässe beachten
Kann jeder Kern Daten mit 15 GB/s kontinuierlich verarbeiten, der Sockel aber nur 60 GB/s liefern, schöpfen etwa vier solche Datenströme die Bandbreite aus. Mehr Kerne können die Datenrate des Sockels nicht erhöhen. Auch Kommunikation über Caches verursacht Aufwand: Ein schreibender Kern ohne Schreibberechtigung muss den Besitz erlangen und andere Kopien invalidieren. Ein Kern, der bereits einen Block in Modified hält, kann lokal weiterschreiben; spätere Leser müssen möglicherweise eine neue Kopie holen.
Wichtige Erkenntnisse
- Gemeinsamer Speicher bedeutet einen Adressraum; private Daten und Caches bleiben möglich.
- Bei NUMA beeinflusst die Datenplatzierung die Rechenleistung.
- Lokale Teilsummen und ein Reduktionsbaum begrenzen die Ressourcenkonkurrenz.
- Barrieren trennen voneinander abhängige Arbeitsphasen.
- Atomare Operationen sichern unteilbare Aktualisierungen; LR/SC wiederholt fehlgeschlagene Versuche.
- Kerne teilen sich die Speicherbandbreite und verursachen Aufwand beim Übertragen des Besitzes.
4.2 Kopien einer Speicherstelle kohärent halten
Private Caches verringern die Latenz, können aber mehrere Kopien derselben Speicherstelle halten. Cachekohärenz verhindert, dass diese Kopien widersprüchliche Folgen von Schreibzugriffen auf diese Stelle zeigen.
Warum die Aktualisierung des Speichers nicht genügt
Zu Beginn steht X = 0 im Speicher. Kern A liest den Wert null und speichert ihn im Cache; Kern B tut dasselbe. A schreibt den Wert eins in seinen Cache mit Rückschreibverfahren. Ohne Kohärenzprotokoll kann B einen Treffer im eigenen Cache haben und weiterhin null lesen. Das Durchschreiben in den Speicher würde Bs bereits zwischengespeicherte Kopie nicht korrigieren.
Kohärenz arbeitet auf der Ebene von Cacheblöcken, üblicherweise 64 Byte. Ein Controller verfolgt den Zustand jedes Blocks und befolgt Protokollregeln, wenn Prozessoren ihn lesen oder schreiben. Migration verschiebt nutzbare Daten in den Cache ihres aktuellen Nutzers. Replikation ermöglicht mehreren Lesern, unveränderte Kopien zu halten. Kohärenz muss beide Vorteile bewahren und zugleich Schreibzugriffe steuern.
Drei erforderliche Eigenschaften
| Eigenschaft | Was sie verlangt |
|---|---|
| Programmreihenfolge für eine Speicherstelle | Ein Prozessor liest seinen eigenen vorherigen Schreibzugriff, wenn kein zwischenzeitlicher Schreibzugriff ihn ersetzt. |
| Schreibpropagierung | Ein Schreibzugriff wird schließlich für die anderen Prozessoren sichtbar. |
| Schreibserialisierung | Alle Prozessoren stimmen in der Reihenfolge der Schreibzugriffe auf diese Stelle überein. |
Wird auf eine Speicherstelle erst der Wert eins und danach der Wert zwei geschrieben, können Leser nicht unterschiedlicher Meinung darüber sein, welcher Schreibzugriff später kam. Erst den späteren und dann den früheren Schreibzugriff zu beobachten, würde diese Reihenfolge umkehren. Ein neuer nachfolgender Schreibzugriff könnte natürlich erneut den Wert eins speichern; Serialisierung betrifft Schreibereignisse und nicht die Frage, ob ein Zahlenwert jemals wieder auftreten darf.
Besitz und Kopien steuern
In einem Schreibinvalidierungsprotokoll erlangt ein schreibender Kern exklusive Schreibberechtigung und invalidiert andere Cachekopien. Ein späterer Lesezugriff auf einen invalidierten Block führt zu einem Fehlzugriff und holt aktuelle Daten. Dies ist ein kohärenzbedingter Fehlzugriff, zusätzlich zu obligatorischen, kapazitätsbedingten und konfliktbedingten Fehlzugriffen.
Ein Schreibaktualisierungsprotokoll sendet neue Werte stattdessen an vorhandene Kopien. Wiederholte Schreibzugriffe können dies aufwendig machen. Snooping sendet Kohärenzanfragen an alle, damit Caches reagieren können. Ein Kohärenzverzeichnis erfasst Mitnutzer und leitet Nachrichten an die betreffenden Caches.
Kohärenz, Reihenfolge und Atomarität
Kohärenz betrifft eine Speicherstelle. Ein Speicherkonsistenzmodell definiert zulässige beobachtbare Reihenfolgen und Ergebnisse von Lese- und Schreibzugriffen, einschließlich Operationen auf verschiedenen Stellen. Sequenzielle Konsistenz verlangt ein Ergebnis, das einer Verschachtelung entspricht, die die Programmreihenfolge jedes Prozessors bewahrt. Schwächere Modelle erlauben mehr Umordnungen; Fence-Befehle erzwingen bei Bedarf die erforderliche Reihenfolge.
Weder kohärente Kopien noch eine konsistente Reihenfolge machen eine aus mehreren Befehlen bestehende Erhöhung atomar. Angenommen, zwei Threads erhöhen einen gemeinsamen Zähler, der anfangs fünf beträgt. Eine mögliche Folge ist: A liest fünf, B liest fünf, A schreibt sechs, B schreibt sechs. Jeder Lese- und Schreibzugriff kann kohärent sein, dennoch beträgt der Endstand sechs statt sieben. Das Programm benötigt eine atomare Aktualisierung oder geeignete Synchronisation, um beide Erhöhungen zu erhalten.
Wichtige Erkenntnisse
- Kohärenz ist auch nötig, wenn Caches in den Speicher durchschreiben.
- Kohärente Systeme bewahren die Programmreihenfolge für eine Speicherstelle, propagieren Schreibzugriffe und serialisieren sie.
- Schreibinvalidierung entfernt andere nutzbare Kopien, bevor ein neuer Besitzer schreibt.
- Invalidierte Daten können kohärenzbedingte Fehlzugriffe verursachen.
- Konsistenzmodelle behandeln die Reihenfolge über verschiedene Speicherstellen hinweg.
- Ein kohärenter Lesezugriff und ein kohärenter Schreibzugriff bleiben zwei getrennte Operationen.
4.3 Besitzwechsel bei MSI und MESI verfolgen
Ein Snooping-Cachecontroller überwacht Kohärenzanfragen auf einer Verbindung mit Rundsendung. Um ein Protokoll zu verfolgen, halte für die Blockkopie jedes Kerns einen Zustand fest und aktualisiere nach jedem Zugriff alle betroffenen Kopien.
Zustände als Berechtigungen lesen
| Zustand | Nutzbare Kopie? | Wer darf sie sonst besitzen? | Ist der Speicher aktuell? |
|---|---|---|---|
| Modified (M) | Ja, Lesen und Schreiben | Keine andere gültige Kopie | Nein |
| Shared (S) | Ja, Lesen | Andere Leser dürfen Kopien besitzen | Ja |
| Invalid (I) | Nein | Andere Caches dürfen Kopien besitzen | Hängt von deren Zustand ab |
| Exclusive (E), nur MESI | Ja, Lesen und stiller Übergang zum Schreiben | Keine andere gültige Kopie | Ja |
Im grundlegenden MSI sendet ein Lesefehlzugriff eine Busleseanfrage und lädt den Block in S. Das Schreiben eines Blocks in S benötigt ein Upgrade, um andere Kopien zu invalidieren. Das Schreiben aus I benötigt eine exklusive Leseanfrage, um Daten zu holen und Besitz zu erlangen. Der schreibende Kern endet in M. Ein Treffer in M benötigt keine Kohärenztransaktion.
Fünf Zugriffe verfolgen
Beginne mit X = 0 im Speicher und A und B jeweils in Invalid.
- A liest X. Eine Busleseanfrage liefert null. Zustände: A = S, B = I.
- B liest X. Eine weitere Busleseanfrage liefert null. Zustände: A = S, B = S.
- A schreibt eins. Sein Upgrade invalidiert B. Zustände: A = M, B = I.
- B liest X. B hat einen Fehlzugriff; A liefert eins und wechselt nach S. Im grundlegenden MSI wird auch der Speicher wieder aktuell. Zustände: A = S, B = S.
- B schreibt zwei. Sein Upgrade invalidiert A. Zustände: A = I, B = M.
Jeder Zugriff in diesem Ablauf benötigt eine Bustransaktion. Der vierte Zugriff ist die wichtige Prüfung des Besitzes: Der Speicher allein enthielt noch den alten Wert, bevor A seine veränderten Daten lieferte.
Was Exclusive spart
MESI ergänzt E für eine einzelne unveränderte Cachekopie. Liest A einen nicht zwischengespeicherten Block und hat kein anderer Cache ihn, erhält A den Zustand E. Ein anschließender Schreibzugriff ändert E stillschweigend in M, weil keine anderen Kopien invalidiert werden müssen. MSI würde zunächst S laden und für diesen Schreibzugriff dann ein Upgrade senden.
Liest ein anderer Kern, während A den Zustand E hält, wechseln beide nach S. M und E haben beide einen gültigen Besitzer, aber nur M enthält Daten, die neuer als der Speicherinhalt sind.
Wiederholte Schreibzugriffe und Skalierung
Ein schreibender Kern, der M erlangt hat, kann den Block zehnmal aktualisieren, ohne zehn neue Invalidierungen auszulösen. Ein Schreibaktualisierungsverfahren würde die geänderten Werte an die Mitnutzer senden.
Snooping mit Rundsendung wird aufwendig, wenn immer mehr Kerne jede Anfrage überwachen. Verzeichnisstrukturen verringern diese Auffächerung. Ausgefeiltere Protokolle ergänzen nützliche Zustände: Der Owned-Zustand von MOESI erlaubt eine veränderte gemeinsam genutzte Kopie, und MESIF bestimmt einen Mitnutzer im Forward-Zustand zur Beantwortung von Anfragen.
Wichtige Erkenntnisse
- MSI verfolgt Kopien in den Zuständen Modified, Shared und Invalid.
- Ein schreibender Kern benötigt exklusiven Besitz; Treffer in Modified bleiben lokal.
- Ein entfernter Lesezugriff veranlasst im grundlegenden MSI den Besitzer in Modified, aktuelle Daten zu liefern und nach Shared zu wechseln.
- Der Exclusive-Zustand von MESI lässt einen einzelnen Leser mit unveränderter Kopie ohne Upgrade-Transaktion schreiben.
- Exclusive und Modified haben beide einen Besitzer, aber nur Modified ist verändert.
4.4 Mitnutzer erfassen und unabhängige Daten trennen
Jede Kohärenzanfrage an alle zu senden, wird auf großen Rechnern aufwendig. Ein Kohärenzverzeichnis erfasst, welche Caches einen Block möglicherweise besitzen, sodass Anfragen an diese Caches gehen können. Dieselbe Erfassung auf Blockebene erklärt auch ein häufiges Leistungsproblem: False Sharing.
Den Heimatknoten eines Blocks fragen
Jeder Block besitzt einen Eintrag im Heimatverzeichnis. Dieser hält fest, ob der Block nicht im Cache, gemeinsam genutzt oder verändert ist und welche Caches ihn besitzen. Heimatknoten sind auf Knoten oder Verzeichnisteile verteilt; so verteilen sich Speicherbedarf und Anfragebearbeitung.
Ein Lesefehlzugriff geht an den Heimatknoten. Hat der Speicher die aktuellen Daten, kann er sie liefern, und das Verzeichnis erfasst den neuen Mitnutzer. Besitzt ein anderer Cache eine Modified-Kopie, leitet der Heimatknoten die Anfrage an diesen Besitzer weiter, damit aktuelle Daten geliefert werden können. In einem grundlegenden Verzeichnisprotokoll wird dadurch auch der Speicher wieder aktuell.
Um ohne bestehenden Besitz im Zustand Modified zu schreiben, fragt ein Kern den Heimatknoten nach Berechtigung. Das Verzeichnis sendet Invalidierungen an erfasste Mitnutzer, wartet auf Bestätigungen und gewährt exklusiven Besitz. Wiederholte Schreibtreffer in M benötigen dann keine weiteren Kohärenznachrichten. Der Umweg über das Verzeichnis erhöht die Latenz, aber Punkt-zu-Punkt-Verkehr vermeidet es, jeden Cache nach jedem Block zu fragen. Snoop-Filter auf dem Chip erfüllen einen verwandten Zweck, indem sie Anfragen auf mögliche Besitzer beschränken.
Speicherbedarf des Verzeichnisses abschätzen
Ein vollständiger Präsenzvektor enthält ein Bit pro Kern und Block. Bei 64 Kernen sind das 64 Bit oder acht Byte. Im Verhältnis zu einem 64 Byte großen Datenblock kosten allein die Präsenzbits , also 12,5 %. Bei 256 Kernen kostet der 32 Byte große Vektor 50 %. Zustandsbits kommen hinzu. Begrenzte Mitnutzerlisten, gröbere Vektoren und die Erfassung nur zwischengespeicherter Blöcke verringern diesen Speicherbedarf.
False Sharing erkennen
True Sharing tritt auf, wenn Kerne Daten über dasselbe Wort austauschen. False Sharing tritt auf, wenn sie auf verschiedene Variablen innerhalb eines Cacheblocks zugreifen und mindestens einer schreibt. Die Variablen sind im Programm unabhängig, aber die Kohärenzeinheit ist größer als jede der beiden Variablen.
Zwei Threads erhöhen getrennte 8 Byte große Zähler an 0x1000 und 0x1008. Bei 64 Byte großen Blöcken sind ihre Blocknummern und . Jeder Besitzwechsel überträgt die Berechtigung für beide Zähler zusammen. Häufige abwechselnde Schreibzugriffe können den Block zwischen Caches hin- und herwandern lassen, obwohl keiner der Threads den Wert des anderen benötigt.
Eine Verschiebung des zweiten Zählers nach 0x1040 legt ihn in Block 65. Padding oder Ausrichtung kann Daten pro Thread trennen; auch die Gruppierung von Werten nach ihrem schreibenden Thread hilft. Ein lokaler Akkumulator, der einmal zurückgeschrieben wird, verringert den Verkehr für Besitzwechsel, ohne jeden Wert auffüllen zu müssen.
Auch ein schreibender Kern und ein Leser einer benachbarten Variablen können von False Sharing betroffen sein: Der schreibende Kern invalidiert den ansonsten nutzbaren Block des Lesers. Zwei Leser allein verursachen es nicht. False Sharing erhöht Verkehr und Verzögerung; es führt nicht selbst dazu, dass Aktualisierungen getrennter Variablen falsche Werte erzeugen. Miss Zugriffsmuster und die Platzierung von Cachezeilen, bevor du eine Datenanordnung änderst.
Wichtige Erkenntnisse
- Kohärenzverzeichnisse senden Nachrichten über den Heimatknoten eines Blocks an die erfassten Besitzer.
- Der Speicherbedarf von Präsenzvektoren wächst sowohl mit der Kernzahl als auch mit der Blockzahl.
- False Sharing betrifft verschiedene Variablen in einem Block und mindestens einen schreibenden Kern.
- Trenne den Besitz durch Padding, Platzierung oder weniger gemeinsame Schreibzugriffe.
5Knoten verbinden und im Maßstab eines Warehouse-Scale Computers betreiben
Über einen einzelnen Rechner mit gemeinsamem Speicher hinaus tauschen Knoten Nachrichten über ein Netzwerk aus. Ein Cluster muss Datenplatzierung und Kommunikation ebenso sorgfältig organisieren wie die Aufteilung der Berechnungen. Im Maßstab eines Warehouse-Scale Computers kommen Stromversorgung, Kühlung und alltägliche Hardwareausfälle zum Netzwerk hinzu. Vergleiche die Netzwerkkapazität anhand eines kritischen Schnitts und beurteile dann mit Energie- und Latenzberechnungen, wie ein großer Rechnerverbund einen zuverlässigen Dienst bereitstellt.
5.1 Knoten mit privatem Speicher und ihre Netzwerke vergleichen
Ein Cluster verbindet vollständige Rechner mit jeweils eigenem Speicher und Betriebssystem. Die Knoten arbeiten durch explizite Nachrichten zusammen. Datenplatzierung und Kommunikationskosten werden daher Teil des Leistungsmodells des Programms.
Daten aufteilen und Ergebnisse zusammenführen
MPI, das Message Passing Interface, gibt jedem Prozess einen Rang und stellt Operationen wie Senden, Empfangen, Rundsenden und Reduzieren bereit. Ein SPMD-Programm kann anhand der Ränge die Daten jedes Prozesses auswählen. Ein Zeiger im privaten Adressraum eines Knotens ist kein gewöhnlicher Verweis über gemeinsamen Speicher auf die Daten eines anderen Knotens.
Sind eine Million Werte bereits auf 100 Knoten verteilt, summiert jeder Knoten lokal 10.000. Eine Baumreduktion führt die Teilsummen in sieben Runden zusammen, denn . Diese Runden umfassen Netzwerknachrichten und nicht nur lokale Additionen. Werden Daten nahe bei der Arbeit gehalten, die sie verarbeitet, sinkt der Übertragungsbedarf.
Cluster skalieren mit Standardkomponenten, und ausgefallene Hardware kann ersetzt werden. Eine Anwendung benötigt nach dem Ausfall eines Knotens möglicherweise dennoch eine Wiederherstellung oder einen Neustart; austauschbare Knoten allein erhalten den Fortschritt einer unterbrochenen Aufgabe nicht.
Mehr als die Verbindungsgeschwindigkeit messen
Latenz ist die Zeit, bis eine Nachricht eintrifft. Verbindungsbandbreite ist die Datenrate einer einzelnen Verbindung. Die Gesamtbandbreite des Netzwerks summiert die Verbindungskapazitäten und beschreibt den idealen Gesamtwert bei Auslastung aller Verbindungen. Die Bisektionsbandbreite summiert die Kapazität über den Schnitt mit der geringsten Kapazität, der die Knoten in gleich große Hälften teilt.
Vergleiche für Verbindungen gleicher Kapazität mit der Bandbreite Netzwerke mit 64 Knoten anhand einer einheitlichen Zählweise für Verbindungen:
| Topologie | Verbindungen | Gesamtkapazität | Bisektionskapazität |
|---|---|---|---|
| Ring | 64 | ||
| 8 × 8-Gitter | 112 | ||
| 8 × 8-Torus | 128 | ||
| 6-dimensionaler Hyperwürfel | 192 | ||
| Vollständig verbunden | 2016 |
Das Gitter hat acht Reihen mit je sieben horizontalen Verbindungen und acht Spalten mit je sieben vertikalen Verbindungen, also 112. Verbindungen zwischen den gegenüberliegenden Rändern beider Dimensionen ergeben die 128 des Torus. Jeder Hyperwürfelknoten hat sechs Nachbarn, was Verbindungen ergibt, weil jede Verbindung an zwei Endpunkten gezählt wird.
Den Schnitt verfolgen und dann die Kosten betrachten
Ein in zwei Hälften geteilter Ring wird von nur zwei Verbindungen geschnitten. Viele Nachrichten können daher an dieser Stelle konkurrieren, obwohl der Ring insgesamt 64 Verbindungen hat. Ein vollständig verbundenes Netzwerk hat Verbindungen zwischen seinen beiden Hälften, benötigt aber insgesamt Verbindungen.
Mehr Verbindungen können die Bandbreite verbessern und die Zahl der Zwischenstationen senken, erhöhen aber den Bedarf an Switch-Ports, Kabeln und Leistungsaufnahme. Gemeinsame Busse haben eine einzige gemeinsam genutzte Kommunikationsressource; Gitter und Tori verteilen Verbindungen über den Raum. Große Cluster verwenden oft mehrstufige vermittelte Netzwerke wie Fat Trees. Ihr Nutzen hängt ebenso vom Verkehrsmuster wie von ihrer Gesamtkapazität ab. Für die Kommunikation zwischen Rechnerhälften zeigt die Bisektion eine Grenze, die die Gesamtbandbreite verbirgt.
Wichtige Erkenntnisse
- Clusterknoten nutzen private Adressräume und explizite Kommunikation.
- MPI-Ränge identifizieren Prozesse; kollektive Operationen führen verteilte Arbeit zusammen.
- Nachrichtenrunden verursachen zusätzlichen Aufwand über die lokale Berechnung hinaus.
- Die Gesamtbandbreite misst die gesamte Verbindungskapazität; die Bisektionsbandbreite misst einen kritischen Schnitt durch den Rechner.
- Netzwerkkapazität steht in einem Zielkonflikt mit Ports, Kabeln, Leistungsaufnahme und Zwischenstationen.
5.2 Ein Rechenzentrum als einen Rechner messen
Ein Warehouse-Scale Computer verbindet Zehntausende Server mit Stromversorgung, Kühlung und einem Netzwerk, um Internetdienste bereitzustellen. Unabhängige Anfragen liefern parallele Arbeit, während Stapelverarbeitungs-Frameworks wie MapReduce große Datenverarbeitungsaufgaben auf Rechner verteilen.
Den Entwurf auf den Dienst abstimmen
Warehouse-Scale-Systeme legen Wert auf Kosten pro Anfrage, Energieeffizienz und Verfügbarkeit. Die Software wiederholt Arbeit, repliziert Daten und leitet Anfragen an ausgefallenen Rechnern vorbei. Ein Supercomputer konzentriert sich häufiger auf ein einzelnes eng gekoppeltes wissenschaftliches Programm und ein schnelles Verbindungsnetz.
Server sind mit Switches an der Spitze ihrer Racks verbunden, die wiederum über höhere Switch-Ebenen verbunden sind. Zusätzliche Ebenen erhöhen die Kommunikationslatenz. Aufwärtsführende Verbindungen sind oft überbucht, ihre Kapazität liegt also unter der Gesamtkapazität der Serververbindungen. Manche nichtblockierenden Netzwerke nach dem Clos-Prinzip bieten auf den oberen Ebenen genügend Kapazität, um diese Einschränkung zu vermeiden.
Vierzig Server mit Verbindungen von je 10 Gbit/s bieten einem Rack-Switch 400 Gbit/s. Vier aufwärtsführende Verbindungen mit je 25 Gbit/s stellen 100 Gbit/s bereit, das Verhältnis beträgt also 4:1. Senden alle Server gleichzeitig aus dem Rack und teilen sich die Kapazität gleichmäßig, erreicht jeder durchschnittlich 2,5 Gbit/s. Ein gering belasteter Server kann mehr nutzen, wenn andere untätig sind.
Anlagenaufwand von nutzbarer Berechnung trennen
Die Effektivität des Energieeinsatzes, PUE, ist die Gesamtenergie der Anlage geteilt durch die Energie der IT-Geräte im selben Zeitraum. Zehn MW gleichbleibende IT-Last und 1,5 MW für Kühlung und Verteilungsverluste ergeben . Der Idealwert ist 1,0. Google meldete für 2025 einen jährlichen PUE-Wert von 1,09 über alle seine Rechenzentren hinweg.
PUE misst den Anlagenaufwand und nicht die nutzbaren Berechnungen pro Joule. Untätige oder ineffiziente Server können innerhalb der IT Energie verschwenden, selbst wenn die Anlage einen ausgezeichneten PUE-Wert hat.
Bei 50.000 Servern mit durchschnittlich 300 W beträgt die IT-Leistungsaufnahme 15 MW. Bei PUE 1,2 beträgt die Leistungsaufnahme der Anlage 18 MW. Über 8.760 Stunden verbraucht sie damit 157.680 MWh; allein der Zusatzaufwand verbraucht 26.280 MWh. Stromversorgungsanlagen, Kühlung, Strom, Server und Gebäude tragen alle zu den Betriebskosten bei.
Geringe Last kann dennoch viel Leistung aufnehmen
Energieproportionalität bedeutet, dass die Leistungsaufnahme proportional zur Last steigen sollte. Ein Server mit 150 W im Leerlauf und 300 W Spitzenleistung, dessen Leistungsaufnahme linear steigt, benötigt bei 30 % Last W. Er benötigt 65 % der Spitzenleistung für 30 % der maximalen Arbeit. Frühere Messungen an Warehouse-Scale-Systemen ergaben, dass viele Server bei etwa 10 % bis 50 % Auslastung arbeiteten. Deshalb ist die Leistungsaufnahme im Leerlauf wichtig.
Ausfälle und langsame Antworten einplanen
Fallen 50.000 Server jeweils durchschnittlich einmal in zehn Jahren aus, sind im Rechnerverbund 5.000 Ausfälle pro Jahr zu erwarten, etwa 14 pro Tag. Diese Erwartung begründet Redundanz und erneute Versuche; sie legt nicht genau 14 Ausfälle für jeden Tag fest.
Eine Anfrage, die Antworten vieler Server benötigt, wartet auf die langsamste erforderliche Antwort. Ist jeder von 100 Servern unabhängig von den anderen mit einer Wahrscheinlichkeit von 1 % langsam, beträgt die Wahrscheinlichkeit, dass keiner langsam ist, . Damit ist mindestens einer mit der Wahrscheinlichkeit langsam. Die Tail-Latenz eines Servers wird bei großer Auffächerung zu einer häufigen Verzögerung der Anfrage.
Wichtige Erkenntnisse
- Beim Entwurf von Warehouse-Scale-Systemen werden Dienstdurchsatz, Energie, Kosten und Verfügbarkeit gegeneinander abgewogen.
- Überbuchte aufwärtsführende Verbindungen begrenzen den gleichzeitigen Datenverkehr aus dem Rack.
- PUE misst den Anlagenaufwand, während die Servereffizienz die nutzbare Arbeit innerhalb der IT misst.
- Die Leistungsaufnahme im Leerlauf macht eine geringe Auslastung kostspielig.
- In großen Rechnerverbünden sind Ausfälle alltäglich, und Auffächerung verstärkt die Tail-Latenz.
Mehrkernprozessoren: Beispiele
1Eine nutzbare Quelle paralleler Arbeit wählen
15 Schritte
- 1EinführungMehr Arbeit ohne einen heißeren Takt
Ein Dienst muss mehr Aufgaben bearbeiten, aber eine höhere CPU-Taktfrequenz würde sein Leistungsbudget überschreiten. Parallele Hardware erledigt innerhalb eines festen Leistungsbudgets mehr. Die Entwickler können Kerne hinzufügen (MIMD), Datenoperationen verbreitern (SIMD) oder mehr Threads bereithalten (Multithreading). Jede Wahl benötigt eine geeignete Arbeitsquelle. Bestimme zuerst, was gleichzeitig laufen kann, und frag dann, wie diese Arbeit die Hardware erreicht und ob der Vorteil eine kürzere Antwortzeit oder eine größere Dienstkapazität (Durchsatz) ist.
- 2ErklärungDie Spannung wirkt im Quadrat
Für die dynamische Leistungsaufnahme gilt näherungsweise . Bei unveränderter Kapazität multipliziert eine Senkung von Spannung und Frequenz auf 85 % die Leistungsaufnahme mit . Diese starke Senkung hilft zu erklären, warum mehrere einfachere Kerne attraktiv sein können. Die Power Wall entstand, als Spannungssenkungen die Wärme und den Leistungsaufwand höherer Taktfrequenzen nicht mehr ausgleichen konnten.
- 3BeispielEine Anfrage von vielen Anfragen unterscheiden
Laufen vier unabhängige Downloads auf vier Kernen, kann der Rechner mehr Downloads pro Sekunde abschließen, selbst wenn jeder Download gleich lange dauert. Das ist ein Durchsatzgewinn durch Arbeit auf Anfrageebene. Wird ein Bild in unabhängige Bereiche aufgeteilt und von vier Threads bearbeitet, kann dieses eine Bild früher fertig werden. Parallele Hardware dient beiden Zielen, aber das Programm muss die passende Arbeit bereitstellen.
- 4ErklärungArbeit innerhalb eines Programms finden
Eine Pipeline überlappt Befehle aus einem Strom: ILP. Eine Arrayoperation wiederholt sich über Elemente hinweg: DLP. Getrennte Threads bieten TLP mit eigenen Ausführungszuständen und einem gemeinsamen Prozessadressraum. Diese Formen können gleichzeitig auftreten. Ein Arrayprogramm mit Threads kann Bereiche auf Kerne verteilen, innerhalb jedes Bereichs SIMD nutzen und diese Befehle durch die Pipeline jedes Kerns ausführen.
- 5HerausforderungZählt Abwechseln?
Ein Kern wechselt zwischen zwei Aufgaben. Beide sind eine Sekunde lang in Bearbeitung, aber ihre Befehle werden nie zum selben Zeitpunkt ausgeführt. Ist das parallele Ausführung?
---reveal---
Es ist nebenläufige Ausführung durch Zeitaufteilung. Parallelität verlangt gleichzeitige Operationen auf getrennten Hardwareressourcen. Ein weiterer Kern könnte diese Gleichzeitigkeit ermöglichen, benötigte aber weiterhin ausführungsbereite Arbeit.
- 6ErklärungFrag dich, wer den nächsten Befehl auswählt
Die flynnsche Taxonomie beginnt mit Befehls- und Datenströmen. Ein herkömmlicher skalarer einzelner Kern ist SISD. Eine breite Operation, die einen Befehl auf mehrere Elemente anwendet, ist SIMD. Unabhängig gesteuerte Prozessoren, die ihre Daten bearbeiten, sind MIMD. MISD kombiniert mehrere Befehlsströme mit einem Datenstrom und hat wenige eindeutige praktische Beispiele. Zähle die Ströme auf einer gewählten Hardwareebene.
- 7BeispielEin Programm, unabhängiger Fortschritt
Vier Arbeitseinheiten teilen anhand ihrer IDs ein Array mit 4.000 Elementen in vier Bereiche mit je 1.000 Elementen auf. Sie führen dasselbe Programm aus, aber eine kann ihre letzte Addition erreichen, während eine andere noch Werte lädt. Das ist SPMD-Software auf MIMD-Hardware. Gemeinsamer Programmtext verlangt keine Befehlsausgabe im Gleichschritt. Ein SIMD-Befehl innerhalb einer beliebigen Arbeitseinheit würde eine weitere Ebene paralleler Ausführung hinzufügen.
- 8HerausforderungDie gewählte Ebene klassifizieren
Eine Mehrkern-CPU mit unabhängig gesteuerten Kernen verwendet in jedem Kern Vektoradditionen. Welche Klassen liegen über die Kerne hinweg und innerhalb einer Vektoraddition vor?
---reveal---
Die Kerne bilden MIMD-Hardware, weil sie Befehle unabhängig auswählen. Jede Vektoraddition ist SIMD, weil ein Befehl auf mehreren Datenelementen arbeitet. Beide Beschreibungen können auf denselben Rechner zutreffen, wenn sie sich auf unterschiedliche Ebenen beziehen.
- 9ErklärungAusführungszustand bereithalten
Ein Hardwarethreadkontext speichert einen Befehlszähler und Register. Mehrere Kontexte können sich die Recheneinheiten und Caches eines Kerns teilen. Wenn eine Speicheranfrage einen Thread warten lässt, kann ein anderer bereiter Thread freie Ausführungskapazität füllen. Das Betriebssystem sieht jeden Kontext als logischen Prozessor. Zwei Kontexte in einem Kern teilen sich weiterhin dessen physische Ausführungsressourcen.
- 10ErklärungWählen, wann Threads den Kern teilen
Feingranulares Multithreading wählt in jedem Taktzyklus einen bereiten Thread. Grobgranulares Multithreading wechselt nach einer langen Wartezeit und verursacht Kosten für den Wechsel und das erneute Füllen der Pipeline. SMT geht auf einem Kern mit Mehrfachausgabe weiter: Befehle verschiedener Threads können im selben Taktzyklus ausgegeben werden. Alle drei Verfahren hängen von bereiter nutzbarer Arbeit ab; keines liefert unbegrenzte zusätzliche Rechenkapazität.
- 11BeispielZuerst die Leerlauflücke messen
Ein Thread rechnet 20 Taktzyklen und wartet dann 200. Seine Periode beträgt 220 Taktzyklen, sodass die aktive Zeit ausmacht. Eine lange Wartezeit lässt die meisten Taktzyklen ungenutzt, wenn kein anderer Thread verfügbar ist. Behalte die gesamte Periode aus Rechnen und Warten im Nenner. Eine Division allein durch die Wartezeit würde ein anderes Verhältnis beschreiben und nicht den Anteil der vergangenen Taktzyklen mit nutzbarer Arbeit.
- 12BeispielDie Lücke mit bereiten Rechenphasen füllen
Bei vier frei versetzten Threads, die jeweils 20 Rechentaktzyklen pro Periode von 220 Taktzyklen beitragen, beträgt die ideale Auslastung . Elf Threads können die Periode füllen: Während einer 200 Taktzyklen wartet, tragen die anderen zehn 200 Rechentaktzyklen bei. Dieses ideale Modell setzt kostenlosen Wechsel und keine zusätzliche Ressourcenkonkurrenz voraus; reale Konkurrenz um Bandbreite und Cache kann weniger nutzbare Arbeit bereithalten.
- 13HerausforderungWürde ein weiterer Kontext das lösen?
Die Recheneinheiten eines Kerns sind bereits vollständig ausgelastet, und seine beiden Threads benötigen dieselben Einheiten. Würde ein weiterer Hardwarethread die Kapazität eines weiteren Kerns bereitstellen? Erkläre es anhand der gemeinsam genutzten Ressource.
---reveal---
Nein. Ein neuer Kontext liefert einen weiteren Befehlsstrom, teilt sich aber weiterhin die ausgelasteten Ausführungseinheiten. Ein weiterer vollständiger Kern ergänzt Ausführungshardware. Multithreading ist besonders nützlich, wenn es Kapazität füllt, die wegen Wartezeiten oder zu weniger bereiter Befehle ungenutzt bleibt.
- 14VerbindungDie Verbesserung des Dienstes ihrer Ursache zuordnen
Der Dienst ergänzt Hardwarekontexte und schließt nun mehr unabhängige Anfragen pro Sekunde ab, während die CPU-Arbeit jeder Anfrage etwas länger dauert. Dieses Ergebnis passt zu einem verbesserten Durchsatz: Bereite Anfragen füllen freie Plätze und teilen sich dabei Ressourcen. Es wäre irreführend, dies als Verbesserung der Latenz einer einzelnen Anfrage zu bezeichnen. Wähle ein Leistungsmaß, das zu der Arbeit passt, deren Abschluss die Nutzer benötigen.
- 15ZusammenfassungDie erste Hardwareentscheidung treffen
Bestimme für eine neue Arbeitslast zuerst unabhängige Befehle, Datenelemente, Threads oder Anfragen. Ermittle dann die Hardwareebene, die ihre Befehlsströme steuert. Soll der Gewinn durch zusätzliche Kontexte entstehen, untersuche, welche Wartezeiten Kapazität freilassen und welche Ressourcen sich die Kontexte teilen. Das bietet eine konkrete Grundlage, um vor der Messung des Ergebnisses mehr Kerne, breitere Operationen oder mehr bereite Threads zu wählen.
2Die Grenze vor dem Hinzufügen von Prozessoren bestimmen
16 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungDrei Gründe, warum zusätzliche Hardware enttäuschen kann
- 2ErklärungDie zeitliche Vergleichsbasis wählen
- 3BeispielNur den parallelen Anteil teilen
- 4KernaussageDer feste Teil bestimmt die Gesamtgrenze
- 5HerausforderungIst das Speedup-Ziel erreichbar?
- 6ErklärungDie Frage nach der Arbeitslast im Blick behalten
- 7BeispielDen Anteil des parallelen Laufs verwenden
- 8BeispielAuf die am stärksten belastete Arbeitseinheit warten
- 9HerausforderungWann verlängern zusätzliche Kerne die Zeit?
- 10ErklärungDie Byte zählen, die den Speicher verlassen
- 11BeispielDie beiden Dächer bestimmen
- 12ErklärungWiederverwendung verschiebt den Kernel nach rechts
- 13HerausforderungWelche Erweiterung hebt die aktuelle Schranke an?
- 14HerausforderungEin schneller Kernel in einem langsamen Programm
- 15TippDen angegebenen Gewinn reproduzierbar machen
- 16ZusammenfassungDie nächste Messung wählen
3Eine Arrayschleife in GPU-Arbeit umwandeln
22 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungMit unabhängigen Arrayelementen beginnen
- 2BeispielVor den Befehlen die Elemente zählen
- 3ErklärungDie Werte nebeneinanderlegen
- 4ErklärungElemente mit einer Maske auswählen
- 5HerausforderungKönnen alle Iterationen gleichzeitig beginnen?
- 6ErklärungDie aktive Länge beschreibt den Durchlauf
- 7BeispielEinen langen Vektor in Durchläufe aufteilen
- 8VerbindungEine Vektoroperation mit einer anderen versorgen
- 9HerausforderungUm die tatsächlich ausgeführte Länge weiterrücken
- 10ErklärungEinen Warp einplanen, während ein anderer wartet
- 11BeispielUnterschiedliche Pfade lassen Lanes untätig
- 12ErklärungMit einer Transaktion viele Threads bedienen
- 13HerausforderungDen Weg zum Gerät einbeziehen
- 14VerbindungAuf eine engere Arbeitslast spezialisieren
- 15BeispielDas Arrayelement eines Threads finden
- 16BeispielDen letzten unvollständigen Block abdecken
- 17ErklärungWarten, bevor der Host Ergebnisse verwendet
- 18BeispielInnerhalb eines Blocks reduzieren
- 19HerausforderungKann ein Block auf alle anderen warten?
- 20KernaussageEine Reduktion ändert die Rechenreihenfolge
- 21HerausforderungEine sichere Zuordnung mit effizientem Zugriff verbinden
- 22ZusammenfassungDen vollständigen Arraypfad prüfen
4Einen gemeinsamen Block vom Leser zum schreibenden Kern verfolgen
19 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungGemeinsame Variablen brauchen weiterhin Koordination
- 2ErklärungEin Adressraum, unterschiedliche Zugriffspfade
- 3BeispielLokale Arbeit vor dem Zusammenführen abschließen
- 4HerausforderungWas geschieht bei einem zwischenzeitlichen Schreibzugriff?
- 5BeispielDen veralteten Cache-Treffer finden
- 6ErklärungÜber die Schreibfolge einer Speicherstelle einig sein
- 7HerausforderungKann Kohärenz beide Erhöhungen bewahren?
- 8VerbindungReihenfolge und Besitz unterscheiden
- 9ErklärungDie Berechtigungen in MSI lesen
- 10BeispielZunächst beiden Kernen unveränderte Kopien geben
- 11BeispielDen Besitz an den schreibenden Kern übertragen
- 12KernaussageExclusive spart eine Anfrage für private Daten
- 13HerausforderungEinen Lese- und zwei Schreibzugriffe verfolgen
- 14ErklärungDie Anfrage über den Heimatknoten senden
- 15BeispielVor der Aufwandsschätzung Bit umrechnen
- 16BeispielFinden, welche Byte den Besitz teilen
- 17HerausforderungMuss der benachbarte Leser schreiben?
- 18HerausforderungEine korrekte Summe mit weniger Konkurrenz berechnen
- 19ZusammenfassungSowohl Programm als auch Block untersuchen
5Daten, Energie und langsame Antworten im Rechnerverbund verfolgen
14 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungEin Knoten ist nun ein vollständiger Rechner
- 2ErklärungArbeit nahe bei ihrer Eingabe platzieren
- 3BeispielTeilsummen in Nachrichtenrunden zusammenführen
- 4ErklärungDen engen Schnitt zählen
- 5HerausforderungEin breiteres Netzwerk bezahlen
- 6ErklärungDie aufwärtsführenden Verbindungen teilen
- 7BeispielKühlung und Verteilung einbeziehen
- 8BeispielGleichbleibende Leistung in Jahresenergie umrechnen
- 9HerausforderungWelche Effizienz hat die Änderung verbessert?
- 10BeispielGeringe Last benötigt weiterhin Leistung
- 11ErklärungKleine Ausfallraten summieren sich im Rechnerverbund
- 12BeispielEine langsame Antwort kann die Anfrage aufhalten
- 13HerausforderungVerkürzen mehr Server jede Anfrage?
- 14ZusammenfassungEin Kennmaß für den Rechnerverbund gezielt wählen
Mehrkernprozessoren: Übungsaufgaben
Set 1: Parallele Arbeit und ihre Kosten erkennen10 Übungsfragen
Frage 1 von 10
Antwort zeigenAntwort ausblenden
Frage 2 von 10
Antwort zeigenAntwort ausblenden
Frage 3 von 10
Antwort zeigenAntwort ausblenden
Frage 4 von 10
Antwort zeigenAntwort ausblenden
Frage 5 von 10
Antwort zeigenAntwort ausblenden
Frage 6 von 10
Antwort zeigenAntwort ausblenden
Frage 7 von 10
Antwort zeigenAntwort ausblenden
Frage 8 von 10
Antwort zeigenAntwort ausblenden
Frage 9 von 10
Antwort zeigenAntwort ausblenden
Frage 10 von 10
Antwort zeigenAntwort ausblenden
Set 2: Skalierungs-, Indizierungs- und Kohärenzregeln anwenden10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 3: Leistungsgrenzen im gesamten System diagnostizieren10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
a[i] = a[i-1] + b[i] von links nach rechts. Warum kann der Compiler aufeinanderfolgende Iterationen nicht direkt durch unabhängige SIMD-Lanes ersetzen?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
vl = 28 zurück. Der Programmierer rückt beide Zeiger um 32 Elemente weiter und zieht 32 von der verbleibenden Anzahl ab. Welcher Datenbereich geht zuerst verloren?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
0x2040; B liest wiederholt einen unabhängigen 8 Byte großen Statuswert an 0x2078. Warum kann B kohärenzbedingte Fehlzugriffe erleiden?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 4: Hardwareverhalten mit Leistungsmodellen verbinden10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
if (i < n) { load_into_shared(); __syncthreads(); combine(); }. In seinem letzten Block erfüllen nur einige Threads die Bedingung. Welche strukturelle Korrektur macht die Blockbarriere sicher?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 5: Die Modelle auf neue Entwurfsentscheidungen übertragen10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
0x3000 und 0x3038. Jeder hat einen eigenen schreibenden Thread. Welche Verschiebung trennt sie in verschiedene Blöcke?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Hängst du bei deiner eigenen Aufgabe fest? Hausaufgabenhilfe
Schlüsselbegriffe
A
Aliasing
Amdahlsches Gesetz
Anfrageparallelität
Arithmetische Intensität
Asynchroner Kernelstart
Atomare Lese-Änderungs-Schreiboperation
Automatische Vektorisierung
B
Barriere
Befehlsparallelität
Bereichsprüfung
Bisektionsbandbreite
C
Cachekohärenz
Cluster
Coalescing
D
Datenparallelität
Domänenspezifischer Beschleuniger
E
Energieeffizienz
Energieproportionalität
Exclusive-Zustand
F
False Sharing
Fan-out
Feingranulares Multithreading
Fetch-and-Add
Flynnsche Taxonomie
G
Globaler Speicher
Granularität
Grid
Grobgranulares Multithreading
Gustafsonsches Gesetz
H
Hardware-Multithreading
Heimatknoten
Host
I
Indizierter Zugriff
Invalid-Zustand
Iterationsübergreifende Abhängigkeit
K
Kernel
Knickpunkt
Kohärenzbedingter Fehlzugriff
Kohärenzverzeichnis
Kontrollflussdivergenz
L
Lastverteilung
Latenzverdeckung
Leistungsgrenze
Logischer Prozessor
M
Maske
MIMD
MISD
Modified-Zustand
MPI
Multiprozessor mit gemeinsamem Speicher
N
Nebenläufigkeit
NUMA
P
Parallele Effizienz
Parallelisierungsaufwand
Parallelität
Power Wall
Präsenzvektor
PUE
R
Rang
Rechengebundener Kernel
Reduktionsbaum
Roofline-Modell
S
Schreibinvalidierungsprotokoll
Schreibpropagierung
Schreibserialisierung
Schwache Skalierung
Sequenzielle Konsistenz
Sequenzieller Anteil
Shared Memory
Shared-Zustand
SIMD
SIMD-Lane
SIMT
Simultanes Multithreading
SISD
Snooping
Speedup
Speichergebundener Kernel
Speicherkonsistenzmodell
SPMD
Starke Skalierung
Streaming Multiprocessor
Strip-Mining
Struktur von Arrays
Superlinearer Speedup
T
Tail-Latenz
Threadblock
Threadparallelität
Torus
True Sharing
U
UMA
Ü
Überbuchung
V
Vektorlänge
Verbindungsbandbreite
Verfügbarkeit
Verkettung
VLEN
W
Warehouse-Scale Computer
Warp
Z
Zugriff mit fester Schrittweite
Über diese Einheit
Erstelle deine eigenen Lernmaterialien
Mach aus deinen Notizen, Folien und Dokumenten Lernleitfäden, Übungsfragen, Karteikarten und 6 weitere Tools.