Betrachten Sie das folgende sehr einfache Computerprogramm:
for i = 1 to n:
y[i] = x[p[i]]
Hier und sind -Glied Arrays von Bytes, und ist eine -Glied Array von Wörtern. Hier ist groß, z. B. (so dass nur ein vernachlässigbarer Teil der Daten in einen beliebigen Cache-Speicher passt).
Angenommen, besteht aus Zufallszahlen , die gleichmäßig zwischen und .
Aus Sicht der modernen Hardware sollte dies Folgendes bedeuten:
- Lesen von ist billig (sequentielles Lesen)
- Lesen ist sehr teuer (random liest, fast alle liest Cache - Misses sind, wir werden jedes einzelne Byte aus dem Hauptspeicher zu holen haben)
- Schreiben ist billig (sequentielles Schreiben).
Und das ist in der Tat, was ich beobachte. Das Programm ist im Vergleich zu einem Programm, das nur sequentielles Lesen und Schreiben ausführt, sehr langsam. Groß.
Nun stellt sich die Frage: Wie gut passt dieses Programm auf moderne Multi-Core-Plattformen?
Meine Hypothese war, dass dieses Programm nicht gut parallelisiert. Schließlich ist der Engpass der Hauptspeicher. Ein einzelner Kern verschwendet bereits die meiste Zeit damit, auf einige Daten aus dem Hauptspeicher zu warten.
Dies war jedoch nicht das, was ich beobachtete, als ich anfing, mit einigen Algorithmen zu experimentieren, bei denen der Engpass eine solche Operation war!
Ich habe einfach die naive for-Schleife durch eine OpenMP-Parallel-for-Schleife ersetzt (im Wesentlichen wird nur der Bereich auf kleinere Teile aufgeteilt und diese Teile auf verschiedenen CPU-Kernen parallel ausgeführt).
Auf Low-End-Computern waren die Beschleunigungen in der Tat gering. Aber auf High-End-Plattformen war ich überrascht, dass ich ausgezeichnete nahezu lineare Beschleunigungen erhielt. Einige konkrete Beispiele (das genaue Timing kann ein bisschen abweichen, es gibt viele zufällige Variationen; dies waren nur schnelle Experimente):
2 x 4-Core-Xeon (insgesamt 8 Kerne): Faktor 5-8-Beschleunigung im Vergleich zur Single-Threaded-Version.
2 x 6-Core Xeon (insgesamt 12 Kerne): Faktor 8-14-Beschleunigung im Vergleich zur Single-Threaded-Version.
Das war völlig unerwartet. Fragen:
Genau warum tut diese Art von Programm parallelisieren so gut ? Was passiert in der Hardware? (Meine derzeitige Vermutung geht in diese Richtung: Die zufälligen Lesevorgänge von verschiedenen Threads sind "pipelined" und die durchschnittliche Rate, mit der Antworten auf diese Fragen erhalten werden, ist viel höher als im Fall eines einzelnen Threads.)
Was ist das richtige theoretische Modell , mit dem wir diese Art von Programmen analysieren (und korrekte Vorhersagen über die Leistung treffen können)?
Bearbeiten: Hier sind nun einige Quellcode- und Benchmark-Ergebnisse verfügbar: https://github.com/suomela/parallel-random-read
- ca. 42 ns pro Iteration (zufälliges Lesen) mit einem einzelnen Thread
- ca. 5 ns pro Iteration (zufälliges Lesen) mit 12 Kernen.