2009-04-23 4 views
4

Ich habe ein Java-Programm, das viele kleine Simulationen ausführt. Es läuft ein genetischer Algorithmus, wobei jede Fitness-Funktion eine Simulation ist, die Parameter auf jedem Chromosom verwendet. Jeder dauert vielleicht 10 Sekunden, wenn er alleine läuft, und ich möchte eine ziemlich große Bevölkerungsgröße (sagen wir 100?) Haben. Ich kann die nächste Simulationsrunde erst beginnen, wenn die vorherige beendet ist. Ich habe Zugriff auf eine Maschine mit einer Menge Prozessoren, und ich frage mich, ob ich etwas tun muss, um die Simulationen parallel laufen zu lassen. Ich habe noch nie etwas explizit für Multicore-Prozessoren geschrieben und ich verstehe, dass es eine entmutigende Aufgabe ist.Wie gut ist die JVM bei der Parallelverarbeitung? Wann sollte ich meine eigenen Threads und Runnables erstellen? Warum können sich Threads störend auswirken?

Also das möchte ich gerne wissen: Inwieweit und wie gut parallelisiert die JVM? Ich habe gelesen, dass es Low-Level-Threads erstellt, aber wie schlau ist es? Wie effizient ist das? Würde mein Programm schneller laufen, wenn ich jede Simulation zu einem Thread machen würde? Ich weiß, dass dies ein riesiges Thema ist, aber könnten Sie mich auf einführende Literatur über Parallelverarbeitung und Java hinweisen?

Vielen Dank!

Update: Ok, ich habe einen ExecutorService implementiert und meine kleinen Simulationen implementieren Runnable und run() -Methoden. Statt dies zu schreiben:

Simulator sim = new Simulator(args); 
sim.play(); 
return sim.getResults(); 

Ich schreibe dies in meinem Konstruktor:

ExecutorService executor = Executors.newFixedThreadPool(32); 

Und dann jedes Mal, wenn ich möchte eine neue Simulation auf den Pool hinzufügen, ich laufe dies:

RunnableSimulator rsim = new RunnableSimulator(args); 
exectuor.exectue(rsim); 
return rsim.getResults(); 

Die RunnableSimulator::run()-Methode ruft die Simulator::play()-Methode auf, beide haben keine Argumente.

Ich glaube, ich bekomme Thread-Interferenz, weil jetzt die Simulationen Fehler aus. Mit einem Fehler meine ich, dass Variablen Werte enthalten, die sie eigentlich nicht sollten. Kein Code aus der Simulation wurde verändert und die Simulation lief vor vielen verschiedenen Argumenten einwandfrei. Die Sim funktioniert so: In jedem Spielzug erhält sie ein Spielstück und durchläuft den gesamten Ort auf dem Spielbrett. Es überprüft, ob der angegebene Ort gültig ist, und falls ja, legt das Stück fest und misst die Güte dieses Brettes. Offensichtlich werden nun ungültige Speicherorte an die Festschreibmethode übergeben, was dazu führt, dass der Index außerhalb der zulässigen Grenzen liegt.

Jede Simulation ist ein eigenes Objekt, oder? Basierend auf dem obigen Code? Ich kann den gleichen Satz von Argumenten an die Klassen RunnableSimulator und Simulator übergeben und die ausführbare Version wird Ausnahmen auslösen. Was denkst du könnte dies verursachen und was kann ich tun, um es zu verhindern? Kann ich einige Codebeispiele in einer neuen Frage zur Verfügung stellen?

+0

Ok, ich habe einen ExecutorService implementiert und meine kleinen Simulationen implementieren Runnable und haben run() -Methoden. Statt dies zu schreiben: Simulator sim = new Simulator(args); sim.play(); return sim.getResults(); Ich schreibe dies: ExecutorService executor = Executors.newFixedThreadPool(32); RunnableSimulator rsim = new RunnableSimulator(args); exectuor.exectue(rsim); return rsim.getResults(); Ich glaube, ich bin immer Thread Störungen, weil jetzt die sims Überschreitungsfehler. – hornairs

+0

Mit dem Fehler out meine ich, dass Variablen Werte sind, die sie wirklich nicht sein sollten. Kein interner Code wurde geändert und bevor die Simulation perfekt über viele verschiedene Argumente lief. Die Simulation funktioniert folgendermaßen: Sie gibt ein Stück und einen Ort an und überprüft, ob der Ort gültig ist, und wenn ja, wird das Stück gebunden. Jetzt werden offensichtlich ungültige Speicherorte an die Commit-Methode übergeben. Ich bin fast sicher, dass jede Sim ihre eigene Instanz ist, die ganze neue rsim() Sache. – hornairs

+0

Was denkst du könnte dies verursachen und was kann ich tun, um das zu verhindern? Kann ich einige Codebeispiele in einer neuen Frage zur Verfügung stellen? – hornairs

Antwort

11

Java Concurrency Tutorial

Wenn Sie nur ein paar Sachen aus zu verschiedenen Themen Laichen, und es ist nicht hin und her zwischen verschiedenen Threads gehen zu sprechen, ist es nicht zu hart; schreibe einfach jede in eine Runnable und übergebe sie an eine ExecutorService.

Sie sollten das gesamte Tutorial überfliegen, aber für diese spezielle Aufgabe, start here.

Grundsätzlich Sie etwas tun, wie folgt aus:

ExecutorService executorService = Executors.newFixedThreadPool(n); 

wobei n die Anzahl der Dinge, die Sie auf einmal ausgeführt werden soll (in der Regel die Anzahl der CPUs). Jeder Ihrer Aufgaben sollte ein Objekt sein, das Runnable implementiert, und Sie dann führen Sie es auf Ihrem ExecutorService:

executorService.execute(new SimulationTask(parameters...)); 

Executors.newFixedThreadPool(n) wird gestartet n Threads, und führen die Aufgaben in eine Warteschlange einfügen, die zu diesen Themen-Feeds. Wenn eine Task beendet wird, ist der Thread, auf dem sie ausgeführt wurde, nicht mehr beschäftigt, und die nächste Task in der Warteschlange wird darauf ausgeführt. Ausführen wird nicht blockiert; es wird die Aufgabe einfach in die Warteschlange stellen und mit der nächsten fortfahren.

Die Sache, auf die Sie achten sollten, ist, dass Sie wirklich keinen veränderlichen Zustand zwischen Aufgaben teilen. Ihre Aufgabenklassen sollten nicht von etwas Veränderbarem abhängen, das zwischen ihnen geteilt wird (d. H. Statische Daten). Es gibt Möglichkeiten, mit dem gemeinsamen veränderbaren Zustand umzugehen (Sperren), aber wenn Sie das Problem vollständig vermeiden können, wird es viel einfacher.

EDIT: Lesen Sie Ihre Änderungen zu Ihrer Frage, es sieht aus, als ob Sie wirklich etwas ein wenig anders wollen. Implementieren Sie statt RunnableCallable. Ihre call() Methode sollte ziemlich genau das gleiche wie Ihre aktuelle run() sein, außer es sollte return getResults();. Dann, submit() es zu Ihrem ExecutorService. Sie erhalten dafür eine Future, mit der Sie testen können, ob die Simulation durchgeführt wurde, und wenn ja, erhalten Sie Ihre Ergebnisse.

+0

Danke gut, mein Herr. Also, wenn ich Low-Level-Threads lese, sollte ich an Dinge denken, die über mein Interesse und meine Kontrolle hinausgehen? Wenn ich Multithreading möchte, muss ich es selbst implementieren? – hornairs

+0

Hinzugefügt ein bisschen mehr Klarheit über das, was vor sich geht. Es ist Multithreading darunter, aber Sie befassen sich nicht direkt damit. Du sagst einfach "das sind unabhängige Aufgaben; führe sie gleichzeitig aus". –

+0

Überprüfen Sie die API. http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ExecutorService.htm Sie schreiben "Runnables", die Ihre Verarbeitung tun, dann können Sie myResult = executorService.invokeAll aufrufen (yourListOfRunnables) und erhalten Sie eine Liste der Ergebnisse, wenn alles fertig ist. Stellen Sie sicher, dass die Runnables nicht statische Variablen usw. = Ergebnisse verwenden executor.invokeAll – KarlP

0

Wenn Sie die gesamte Zeit in Ihren Threads verarbeiten, profitieren Sie nicht davon, dass mehr Threads als Prozessoren vorhanden sind. Wenn Ihre Threads gelegentlich auf einander oder auf dem System warten, kann Java bis zu Tausenden von Threads skalieren.

Ich schrieb eine App, die in wenigen Minuten ein Klasse-B-Netzwerk (65.000) entdeckte, indem ich jeden Knoten anpingen, und jeder Ping hatte Versuche mit zunehmender Verzögerung. Wenn ich jeden Ping auf einen separaten Thread setzte (das war vor NIO, könnte ich wahrscheinlich jetzt verbessern), könnte ich zu etwa 4000 Threads in Windows laufen, bevor die Dinge flockig wurden. Linux war die Nummer näher 1000 (Niemals herausgefunden warum).

Egal, welche Sprache oder Toolkit Sie verwenden, wenn Ihre Daten interagiert, müssen Sie einige Aufmerksamkeit auf die Bereiche, in denen es tut. Java verwendet ein Synchronized-Schlüsselwort, um zu verhindern, dass zwei Threads gleichzeitig auf einen Abschnitt zugreifen. Wenn Sie Ihr Java auf eine funktionellere Art und Weise schreiben (alle Ihre Mitglieder final machen), können Sie ohne Synchronisation laufen, aber es kann sein - naja, sagen wir, dass das Lösen von Problemen einen anderen Ansatz hat.

Java verfügt über andere Tools zum Verwalten von Einheiten unabhängiger Arbeit. Weitere Informationen finden Sie im "Concurrent" -Paket.

+0

„Linux die Zahl näher 1000 wurde (nie herausgefunden, warum)“ - ich glaube, ich einmal das gleiche Problem hatte und fand heraus, dass der Linux-Kernel hat eine (konfigurierbare) maximale Anzahl von Betriebssystem-Threads mit einem Standardwert von 1024. –

0

Java ist ziemlich gut bei der Parallelverarbeitung, aber es gibt zwei Einschränkungen:

  • Java-Threads sind relativ Schwergewicht (im Vergleich mit beispielsweise Erlang), so fangen sie nicht in den Hunderten oder Tausenden zu schaffen. Jeder Thread erhält seinen eigenen Stack-Speicher (Standard: 256 KB) und Sie könnten unter anderem nicht genügend Arbeitsspeicher haben.
  • Wenn Sie auf einer sehr leistungsfähigen Maschine laufen (besonders mit vielen CPUs und viel RAM), dann können die Standardeinstellungen der VM (insbesondere in Bezug auf GC) zu suboptimaler Leistung führen und Sie müssen möglicherweise einige Zeit tuning sie über command line options. Leider ist dies keine einfache Aufgabe und erfordert viel Wissen.
1

Java-Threads sind einfach zu schwer. Wir haben parallele Zweige in Ateji PX als sehr leichte geplante Objekte implementiert. Wie in Erlang können Sie zig Millionen parallele Zweige erstellen, bevor Sie einen Overhead bemerken. Aber es ist immer noch Java, so dass Sie nicht zu einer anderen Sprache wechseln müssen.

+0

Wie haben Sie festgestellt, dass Java-Threads zu schwer waren? – Gray

Verwandte Themen