2008-12-02 8 views
7

Kurze Version ist im Titel.Parallelisierung: Was bewirkt, dass Java-Threads andere als Synchronisierung & I/O blockieren?

Lange Version: Ich arbeite an einem Programm zur wissenschaftlichen Optimierung mit Java. Die Arbeitslast des Programms kann in parallele und serielle Phasen unterteilt werden - parallele Phasen, was bedeutet, dass stark parallelisierbare Arbeit ausgeführt wird. Um das Programm zu beschleunigen (es läuft Stunden/Tage), erstelle ich eine Anzahl von Threads, die der Anzahl der CPU-Kerne auf der Maschine entspricht, die ich verwende - typischerweise 4 oder 8 - und teile die Arbeit zwischen ihnen. Ich starte dann diese Threads und verbinde() sie, bevor ich zu einer seriellen Phase übergehe.

So weit so gut. Was mich stört ist, dass die CPU-Auslastung und die Beschleunigung der parallelen Phasen nicht annähernd dem "theoretischen Maximum" entspricht - z. Wenn ich 4 Kerne habe, erwarte ich irgendwo zwischen 350-400% "Auslastung" (wie oben berichtet), aber stattdessen springt es zwischen 180 und 310 um. Mit nur einem Thread bekomme ich 100% CPU-Auslastung.

Die einzigen Gründe, warum ich von für Threads wissen nicht mit voller Geschwindigkeit auszuführen, sind: blockier wegen I/O blockier aufgrund Synchronisation

keine E/A überhaupt in meiner parallelen Threads vorgeht , noch irgendeine Synchronisation - die einzigen Datenstrukturen, die von den Threads geteilt werden, sind schreibgeschützt und sind entweder Grundtypen oder (nicht gleichzeitige) Sammlungen. Ich suche nach anderen Erklärungen. Eine Möglichkeit wäre, dass mehrere Threads für die Garbage-Collection wiederholt blockiert werden, aber das scheint nur in einer Situation mit Speicherdruck sinnvoll zu sein, und ich ordne viel über den erforderlichen maximalen Heap-Platz zu.

Irgendwelche Vorschläge würden geschätzt.

Update: Nur für den Fall, dass jemand neugierig ist, nach einigen weiteren Untersuchungen habe ich den Code für die allgemeine Leistung optimiert und sehe bessere Auslastung, obwohl nichts, was ich geändert habe, mit der Synchronisation zu tun hat. Einige der Änderungen sollten jedoch zu weniger neuen Heapzuweisungen geführt haben, insbesondere habe ich die Verwendung von Iteratoren und temporary boxed Nummern (Die CERN "Colt" -Bibliothek für Hochleistungs-Java-Computing war hier nützlich: it bietet Sammlungen wie IntArrayList, DoubleArrayList usw. für grundlegende Typen.). Also ich denke, Müllsammlung war wahrscheinlich der Schuldige.

+0

Joe, da ich neugierig bin, was Sie sagen, ist, dass nach der Optimierung sehen Sie eine bessere CPU-Auslastung.Was sind die Zahlen? – Dan

Antwort

5

Alle Grafikoperationen werden in einem einzelnen Thread ausgeführt. Wenn sie auf dem Bildschirm gerendert werden, konkurrieren sie effektiv um den Zugriff auf diesen Thread.

Wenn Sie unter Windows laufen, laufen alle Grafikoperationen in einem einzigen Thread, egal was passiert. Andere Betriebssysteme haben ähnliche Einschränkungen.

Es ist manchmal ziemlich schwierig, die richtige Granularität von Thread-Arbeitern zu erhalten, und manchmal ist es einfach, sie zu groß oder zu klein zu machen, was normalerweise weniger als 100% aller Kerne ergibt.

Wenn Sie nicht viel GUI rendern, ist der wahrscheinlichste Täter, dass Sie mehr konkurrieren, als Sie für eine freigegebene Ressource denken. Dies ist leicht mit Profiler-Tools wie jprofiler zu sehen. Einige VMs wie beas jrockit können dir das direkt aus der Box erzählen.

Dies ist einer dieser Orte, wo Sie nicht auf Raten arbeiten wollen. Holen Sie sich einen Profiler!

+0

Das ist ein guter Vorschlag. Javas eingebauter Profiler sagt, soweit ich das beurteilen kann, nichts hilfreiches im Zusammenhang mit Konkurrenz, aber wenn JProfiler das tut, werde ich darüber nachdenken, es zu kaufen. Wie genau würde eine Konkurrenz über eine gemeinsame Ressource offensichtlich sein? – Joe

4

Zunächst wird GC nicht nur "in der Situation mit Speicherdruck" geschehen, sondern zu jeder Zeit die JVM sieht fit (unvorhersehbar, soweit ich weiß).

Zweitens, wenn Ihre Threads Speicher im Heap zuweisen (Sie erwähnen, dass sie Collections verwenden, so rate ich ihnen Speicher im Heap zuordnen), können Sie nie sicher sein, wenn dieser Speicher derzeit im RAM oder auf einer virtuellen Arbeitsspeicher-Seite ist (Das Betriebssystem entscheidet), und somit kann der Zugriff auf "Speicher" Blockierung von E/A-Zugriff erzeugen!

Schließlich, wie in einer vorherigen Antwort vorgeschlagen, kann es nützlich sein, zu überprüfen, was passiert, indem Sie einen Profiler verwenden (oder sogar JMX-Überwachung könnte einige Hinweise geben).

Ich glaube, es wird schwierig sein, weitere Hinweise zu Ihrem Problem zu erhalten, wenn Sie nicht konkretere (Code-) Informationen bereitstellen.

0

Sie versuchen, die volle CPU-Fähigkeit für Ihre Berechnungen zu verwenden, aber das Betriebssystem selbst verwendet ebenfalls Ressourcen. Seien Sie sich daher bewusst, dass das Betriebssystem einen Teil Ihrer Ausführung blockiert, um seine Anforderungen zu erfüllen.

+0

Es sollte nicht so viel Zeit in Anspruch nehmen, wie Joe es sieht - ich hoffe, 370% zu sehen, es sei denn, er macht etwas ganz anderes auf der Box. –

+0

Natürlich, aber er wird nie 400% sehen, weil das Betriebssystem einige (wenn auch kleine) Dinge tun muss. – boutta

2

Erstens nehme ich an, dass Sie keine andere wichtige Arbeit an der Box machen. Wenn Sie sind, wird das eindeutig Dinge durcheinander bringen.

Es klingt sehr merkwürdig, wenn Sie wirklich teilen nichts sind. Können Sie uns mehr darüber sagen, was der Code wirklich macht?

Was passiert, wenn Sie n Kopien des Programms als verschiedene Java-Prozesse ausführen, wobei jeder nur einen einzigen Thread verwendet? Wenn das jede CPU komplett nutzt, dann wissen wir zumindest, dass es kein Problem mit dem Betriebssystem sein kann. Apropos OS, auf welchem ​​läuft es und welche JVM? Wenn Sie verschiedene JVMs und verschiedene Betriebssysteme ausprobieren können, geben die Ergebnisse möglicherweise einen Hinweis, was falsch ist.

+0

Gute Idee, sollten Sie auf jeden Fall Lauf n Kopien statt n Threads überprüfen. – SCdF

1

Auch ein wichtiger Punkt: Welche Hardware verwenden Sie? Zum Beispiel 4-8 Kerne könnten bedeuten, dass du an einer Suns Niagara CPU arbeitest. Und obwohl sie 4-8 Kerne haben, haben sie weniger FPU s. Bei der Berechnung von wissenschaftlichen Daten kann es passieren, dass die FPU der Flaschenhals ist.

+0

Warten auf eine FPU, oder Speicher zu kommen, wird immer noch als CPU-Auslastung zählen. Niagara II hat eine FPU pro Kern. –

+0

Der Niagara II ist in der Tat besser und hat mehr, aber ich bin nicht sicher, wie die CPU-Nutzung von blockierten FPU für die Prozesszeit berücksichtigt wird. – flolo

0

Sie tun Synchronisierung auf einer Ebene.

Vielleicht nur im Speicherzuweisungssystem, einschließlich Garbage Collection. Während der JVM-Anbieter daran gearbeitet hat, in diesen Bereichen auf ein Minimum zu beschränken, kann er ihn nicht auf Null reduzieren. Vielleicht ist etwas an Ihrer Anwendung ein Schwachpunkt in diesem Bereich.

Die akzeptierte Weisheit ist, "bauen Sie nicht Ihren eigenen Speicher, der Pool zurückfordert, lassen Sie den GC für Sie arbeiten". Dies ist meistens der Fall, aber nicht in mindestens einem Codeabschnitt, den ich beibehalte (nachgewiesen durch Profiling). Vielleicht müssen Sie Ihre Objektzuordnung etwas überarbeiten.

0

Probieren Sie den Latenzanalysator aus, der mit JRockit Mission Control geliefert wird. Es wird Ihnen zeigen, was die CPU tut, wenn sie nichts tut, wenn die Anwendung auf Datei-I/O, TLA-Fetches, Objektzuweisungen, Thread-Aussetzung, JVM-Sperren, GC-Pausen etc. wartet. Sie können auch Übergänge sehen , z.B wenn ein Thread einen anderen aufweckt. Der Overhead ist vernachlässigbar, 1% oder so.

Weitere Informationen finden Sie unter blog. Das Tool ist kostenlos für die Entwicklung und Sie können es herunterladen here