Ich bekomme meine nassen Füße und schreibe gleichzeitig Programme in Haskell mit GHC für Multicore-Maschinen. Als ersten Schritt habe ich beschlossen, ein Programm zu schreiben, das gleichzeitig in ein IOArray liest und schreibt. Ich hatte den Eindruck, dass das Lesen und Schreiben in IOArray keine Synchronisation mit sich bringt. Ich mache das, um eine Basislinie zu erstellen, die mit der Leistung anderer Datenstrukturen verglichen werden kann, die geeignete Synchronisationsmechanismen verwenden. Ich bin auf überraschende Ergebnisse gestoßen, nämlich dass ich in vielen Fällen überhaupt keine Geschwindigkeit bekomme. Dies lässt mich fragen, ob es eine Low-Level-Synchronisation in der Ghc-Laufzeit gibt, zum Beispiel Synchronisation und Blockierung bei der Auswertung von Thunks (d. H. "Schwarze Löcher"). Hier sind die Details ...
Ich schreibe ein paar Variationen über ein einzelnes Programm. Der Grundgedanke ist, dass ich eine DirectAddressTable-Datenstruktur geschrieben habe, die einfach ein Wrapper um ein IOArray ist, der Einfüge- und Suchmethoden bereitstellt:
%Vor%Ich habe dann ein Hauptprogramm, das eine neue Tabelle initialisiert, einige Threads abfängt, wobei jeder Thread eine feste Anzahl von Werten in die gerade initialisierte Tabelle schreibt und liest. Die Gesamtzahl der zu schreibenden Elemente ist festgelegt. Die Anzahl der zu verwendenden Threads wird von einem Befehlszeilenargument übernommen, und die zu verarbeitenden Elemente werden gleichmäßig unter den Threads aufgeteilt.
%Vor%Ich habe das mit ghc 7.2.1 (ähnliche Ergebnisse mit 7.0.3) mit "ghc --make -rtsopts -drawed -fforce-recp -O2 DirectTableTest.hs" kompiliert. Das Ausführen von "time ./DirectTableTest 1 + RTS -N1" dauert etwa 1,4 Sekunden und das Ausführen von "time ./DirectTableTest 2 + RTS -N2" dauert etwa 2,0 Sekunden! Die Verwendung eines Kerns mehr als Worker Threads ist ein wenig besser, mit "Zeit ./DirectTableTest 1 + RTS -N1" dauert etwa 1,4 Sekunden und "Zeit ./DirectTableTest 1 + RTS -N2" und "Zeit ./DirectTableTest 2 + RTS -N3 "beide etwa 1,4 Sekunden. Die Ausführung mit der Option "-N2 -s" zeigt, dass die Produktivität bei 95,4% und die GC bei 4,3% liegt. Wenn ich eine Ausführung des Programms mit ThreadScope ansehe, sehe ich nichts alarmierendes. Jeder HEC ergibt einmal pro ms, wenn ein GC auftritt. Laufen mit 4 Kernen ergibt eine Zeit von etwa 1,2 Sekunden, was mindestens ein bisschen besser ist als 1 Kern. Mehr Kerne verbessern sich nicht.
Ich habe festgestellt, dass das Ändern des in der Implementierung von DirectAddressTable von IOArray in IOUArray verwendeten Array-Typs dieses Problem behebt. Mit dieser Änderung beträgt die Laufzeit von "time ./DirectTableTest1 + RTS -N1" etwa 1,4 Sekunden, während die laufende "time ./DirectTableTest2 + RTS -N2" etwa 1,0 Sekunden beträgt. Das Erhöhen auf 4 Kerne ergibt eine Laufzeit von 0,55 Sekunden. Laufen mit "-s" zeigt eine GC-Zeit von% 3,9 Prozent. Unter ThreadScope kann ich sehen, dass beide Threads alle 0,4 ms häufiger kommen als im vorherigen Programm.
Schließlich habe ich noch eine Variante versucht. Anstatt, dass die Threads auf demselben freigegebenen Array arbeiten, musste ich jeden Thread auf einem eigenen Array arbeiten. Das skaliert (wie zu erwarten) mehr oder weniger wie das zweite Programm, wobei entweder IOArray oder IOUArray die DirectAddressTable-Datenstruktur implementiert.
Ich verstehe, warum IOUArray besser funktioniert als IOArray, aber ich weiß nicht, warum es besser auf mehrere Threads und Kerne skaliert. Weiß jemand, warum dies passiert oder was ich tun kann, um herauszufinden, was vor sich geht? Ich frage mich, ob dieses Problem auf mehrere Threads zurückzuführen sein könnte, die bei der Bewertung desselben Thunks blockieren und ob es damit in Zusammenhang steht: Ссылка .
Das Ausführen der Zeit ./DirectTableTest 1 + RTS -N1 dauert ungefähr 1,4 Sekunden und das Ausführen der Zeit ./DirectTableTest 2 + RTS -N2 dauert ungefähr 2,0 Sekunden!
Ich kann Ihre Ergebnisse nicht reproduzieren:
%Vor%Und dies scheint wie erwartet zu skalieren, da die Anzahl der leichten Fäden ebenfalls zunimmt:
%Vor% Haben Sie eigentlich 2 CPUs? Wenn Sie mit mehr GHC-Threads ( -Nx
) als mit verfügbaren CPUs arbeiten, sind Ihre Ergebnisse sehr schlecht. Was ich wirklich frage ist: Sind Sie sicher, dass keine anderen CPU-intensiven Prozesse auf Ihrem System laufen?
Wie für das IOUArray (durch Bearbeiten)
Ich verstehe, warum IOUArray besser funktioniert als IOArray, aber ich weiß nicht, warum es besser auf mehrere Threads und Kerne skaliert
Ein ungeboxtes Array wird zusammenhängend sein und somit viel mehr vom Caching profitieren. Boxed-Werte, die an willkürlichen Speicherorten auf dem Heap gespeichert sind, könnten zu einer starken Zunahme von Cache-Invalidierungen zwischen den Kernen führen.
Tags und Links haskell ghc concurrency