2011-01-08 10 views
1

Ich schreibe einen ziemlich CPU-intensiven, parallelen numerischen Code, der große Datenmengen verarbeiten wird, die in Java-Arrays gespeichert sind (z. B. viele doppelte [100000] s). Einige der Algorithmen können meh- rere Male über mehrere Tage laufen, so dass eine maximale stationäre Leistung eine hohe Priorität hat.Optimieren der Verarbeitung und Verwaltung großer Java-Datenarrays

Im Wesentlichen ist jeder Algorithmus ein Java-Objekt, das eine Methode API so etwas hat:

public double[] runMyAlgorithm(double[] inputData); 

oder alternativ eine Referenz auf das Array übergeben könnte die Ausgangsdaten zu speichern:

public runMyAlgorithm(double[] inputData, double[] outputData); 

Angesichts dieser Anforderung versuche ich, die optimale Strategie für die Zuweisung/Verwaltung von Array-Speicherplatz zu bestimmen. Häufig benötigen die Algorithmen große Mengen an temporärem Speicherplatz. Sie nehmen auch große Arrays als Eingabe und erstellen große Arrays als Ausgabe.

Unter den Optionen Ich erwäge sind:

  • immer neue Arrays als lokale Variablen zuweisen, wenn sie (zum Beispiel neue Doppel [100000]) benötigt werden. Wahrscheinlich der einfachste Ansatz, aber wird eine Los Müll produzieren.
  • Temporäre Arrays vorab zuweisen und sie als letzte Felder im Algorithmusobjekt speichern - großer Nachteil wäre, dass dies bedeuten würde, dass nur ein Thread den Algorithmus zu einem bestimmten Zeitpunkt ausführen könnte.
  • Behalten Sie zuvor zugewiesene temporäre Arrays im ThreadLocal-Speicher bei, sodass ein Thread bei Bedarf einen festen Anteil an temporärem Array-Speicherplatz verwenden kann. ThreadLocal wäre erforderlich, da mehrere Threads denselben Algorithmus gleichzeitig ausführen.
  • Übergeben Sie viele Arrays als Parameter (einschließlich der temporären Arrays für den Algorithmus). Nicht gut, da es die Algorithmus-API extrem hässlich macht, wenn der Aufrufer für die Bereitstellung von temporärem Array-Platz verantwortlich sein muss.
  • Verteilen Sie extrem große Arrays (z. B. double [10000000]), stellen Sie aber auch den Algorithmus mit Offsets zur Verfügung Array, so dass verschiedene Threads unabhängig voneinander einen anderen Bereich des Arrays verwenden. Offensichtlich wird Code benötigt, um die Offsets und die Zuweisung der Array-Bereiche zu verwalten.

Irgendwelche Gedanken darüber, welcher Ansatz am besten wäre (und warum)?

Antwort

2

Was ich bei der Arbeit mit Speicher in Java festgestellt habe, ist folgendes. Wenn Ihr Speicher Muster braucht, die einfach sind (meistens 2-3 Arten von Speicherzuweisungen), können Sie normalerweise besser sein als der Standardzuordner. Sie können entweder beim Start der Anwendung einen Pool von Puffern vorbelegen und sie nach Bedarf verwenden oder zur anderen Route gehen (am Anfang ein riesiges Array zuweisen und Teile davon bei Bedarf bereitstellen). In der Tat schreiben Sie Ihren eigenen Speicherzuordner. Aber die Chancen stehen gut, dass Sie einen schlechteren Job machen als der Standard-Allokator von Java.

Ich würde wahrscheinlich versuchen, Folgendes zu tun: Standardisieren Sie die Puffergrößen und normal zuweisen. Auf diese Weise wird nach einer Weile die einzige Speicherzuweisung/-freigabe in festen Größen sein, was dem Garbage Collector sehr helfen wird, schnell zu laufen. Eine weitere Sache, die ich tun würde, ist sicherzustellen, dass bei der Entwurfszeit des Algorithmus der gesamte Speicher, der an einem bestimmten Punkt benötigt wird, nicht mehr als 80-85% des Speichers der Maschine übersteigt, um nicht versehentlich eine vollständige Sammlung auszulösen.

Abgesehen von diesen Heuristiken würde ich wahrscheinlich die Hölle jeder Lösung testen, die ich wählen würde und sehen, wie es in der Praxis funktioniert.

2

Die Zuweisung großer Arrays ist für den GC relativ günstig. Sie neigen dazu, Sie schnell in Eden zu benutzen, aber die Kosten sind weitgehend pro Objekt. Ich schlage vor, Sie schreiben den Code so einfach wie möglich und optimieren ihn später, nachdem Sie die Anwendung erstellt haben. Ein Double [100000] ist weniger als ein MB und Sie können über 1000 in einem GB.

Speicher ist viel billiger als früher. Ein 8-GB-Server kostet etwa £ 850. Ein 24-GB-Server kostet etwa 1.800 GBP. (eine 24-GB-Maschine könnte Ihnen 24K x double [100000] ermöglichen) Sie können feststellen, dass die Verwendung einer großen Heap-Größe oder sogar einer großen Eden-Größe Ihnen die gewünschte Effizienz bietet.

Verwandte Themen