Mono-Multiprocessing-Leistungsproblem

8

Ich habe schwerwiegende Leistungsprobleme beim Ausführen von rechenintensivem multiprocessed Code auf Mono. Das folgende einfache Snippet, das den Wert von pi mit Monte-Carlo-Methoden schätzt, zeigt das Problem.

Das Programm erzeugt eine Anzahl von Threads, die der Anzahl der logischen Kerne auf der aktuellen Maschine entspricht, und führt eine identische Berechnung für jede aus. Wenn der gesamte Prozess auf einem Intel Core i7-Laptop mit Windows 7 unter Verwendung von .NET Framework 4.5 ausgeführt wird, wird der gesamte Prozess in 4,2 Sekunden ausgeführt, und die relative Standardabweichung zwischen den jeweiligen Ausführungszeiten der Threads beträgt 2%.

Wenn jedoch auf demselben Computer (und Betriebssystem) mit Mono 2.10.9 gearbeitet wird, kann die gesamte Ausführungszeit bis zu 18 Sekunden dauern. Es gibt eine große Varianz zwischen den Performances der jeweiligen Threads, wobei die schnellste in nur 5,6 Sekunden abgeschlossen ist, während die langsamste 18 Sekunden dauert. Der Durchschnitt ist 14s und die relative Standardabweichung beträgt 28%.

Die Ursache scheint nicht Thread-Planung zu sein. Festhalten jedes Threads an einem bestimmten Kern (durch Aufruf von BeginThreadAffinity und SetThreadAffinityMask ) hat keine signifikante Auswirkung auf die Dauern oder Varianzen der Threads.

Gleichermaßen gibt die Berechnung der einzelnen Threads mehrere Male (und sie einzeln zu timpfen) auch scheinbar Ad-hoc-Dauer. Daher scheint das Problem auch nicht von den Aufwärmzeiten pro Prozessor verursacht zu werden.

Was ich gefunden habe, um einen Unterschied zu machen, war, alle acht Threads an den gleichen Prozessor anzuheften. In diesem Fall betrug die Gesamtausführung 25s, was nur 1% langsamer ist als die Ausführung von 8 × der Arbeit an einem einzelnen Thread. Außerdem sank die relative Standardabweichung auf unter 1%. Somit liegt das Problem nicht in Monos Multithreading per se, sondern in seinem Multiprocessing.

Hat jemand eine Lösung zur Behebung dieses Leistungsproblems?

%Vor%

Ausgabe auf .NET:

%Vor%

Ausgabe bei Mono:

%Vor%

Ausgabe auf Mono, wobei alle Threads an denselben Prozessor angeheftet sind:

%Vor%

Ausgabe auf Mono, einzelner Thread:

%Vor%     
Douglas 09.07.2013, 17:51
quelle

1 Antwort

5

Das Ausführen mit mono --gc=sgen hat es wie erwartet korrigiert (mit Mono 3.0.10).

Das zu Grunde liegende Problem ist, dass die thread-lokale Zuordnung für den Boohm-Garbage Collector eine spezielle Abstimmung erfordert, wenn sie in Verbindung mit typisierter Zuweisung oder großen Blöcken verwendet wird. Dies ist nicht nur ein wenig nicht trivial, sondern hat auch einige Nachteile: entweder machen Sie die Markierung komplizierter / teurer oder Sie benötigen eine Freelist pro Thread und Typ (gut, pro Speicherlayout).

Standardmäßig unterstützt der Boehm GC nur vollständig zeigerfreie Speicherbereiche oder Bereiche, in denen jedes Wort ein Zeiger sein kann, bis zu maximal 256 Bytes.

Aber ohne thread-lokale Zuweisung erhält jede Zuweisung eine globale Sperre, die zu einem Engpass wird.

Der SGen-Garbage-Collector ist für Mono maßgeschneidert und wurde speziell entwickelt, um in einem Multi-Thread-System schnell zu funktionieren, und hat diese Probleme nicht.

    
Reimer Behrends 09.07.2013, 18:13
quelle