Für ein Projekt kommt GridGain als Framework zur verteilten Berechnung zum Einsatz. Die Wahl viel auf Gridgain, da es frei verfügbar ist, dank 100% Java auf vielen Plattformen verfügbar ist und auch weil Dokumentation und der GridGain Community Support herausragend erschienen.

GridGain selber ist schnell eingerichtet, und auch der Einstieg ist aufregend einfach und mit schnellen Erfolgserlebnissen garniert. Besonders gefällt der P2P Classloader: Grid Knoten benötigen im Prinzip lediglich Gridgain – benötigte Klassen werden mittels Java Serialisierung (bzw. Jboss Serialization) verteilt, und erlauben so ein sehr dynamisches Deployment. (Vor allem interessant wenn es um Entwicklung und Testen geht!

Wo viel Licht – da auch Schatten?

Ganz so schlimm war es dann doch nicht, doch es hat ein paar Tage gedauert. Das Problem: Egal wie groß der zur Verfügung gestellte Speicher für die GridGain Knoten war, es hagelte spätestens nach ein bis zwei Minuten Java OutOfMemory Errors. Und das bei Bereichen von 2GB Speicher – lokal gestartet kam die Anwendung mit rund 256MB locker aus.

Der Verdacht lag nahe, dass es sich hier um ein Memory Leak handelt. (Sehr scharfsinnig…) Die Ursache war jedoch nicht so schnell aufgetrieben. Sicher war wohl, dass GridGain hier wohl kaum Schuld sein würde, ebenfalls war ich sicher, dass der von mir geschriebene Code nicht so dramatisch schlecht sein würde.

Blieb nur noch der BLOB von Dritten. Neuronale Netze, von Fortran nach C, dann über C# nach Java „portiert“. Der blanke Horror schaut einem ins Gesicht, Dokumentation ist natürlich nicht vorhanden. („Das macht die Dateien ja nur größer.“) Sprechende Variablen oder Methoden – nein, unnötige Tipparbeit. Eine Chance für den Netbeans Profiler, der mir vor allem wegen der kostenlosen und einfachen Verfügbarkeit gefallen hat.

Der Profiler kommt mit allem, was ich brauchte: Sowohl CPU als auch Speicher Profiling sind möglich, und ich konnte mich auf die „hot spots“ beschränken, statt mich tief in fremden Quälcode zu knien. Die Arbeit mit dem Netbeans Profiler macht sogar richtig Spaß – schnell sind die heißen Punkte identifiziert, dank integrierter Stacktraces und einer Übersicht welche Objekte Referenzen halten und den Garbage Collector begrenzen bekommt man ein gutes Gefühl für den zu analysierenden Code.

Ziel war zwar nicht das Programm schneller zu machen, sondern vor allem die Verteilung mittels GridGain sicherzustellen, doch dabei wurde das Programm dann noch an zwei Stellen von O(n) Datenstrukturen auf O(log n) umgestellt. Ersparnis: ~95%

Der Schuldige für die OutOfMemory Errors mit Gridgain war dann auch bald identifiziert: Für jeden Durchlauf des neuronalen Netzes wurde die Trainingsdatenbank bzw. das neuronale Netz neu geladen und initialisiert. Hier kann GridGain nicht viel machen, als die Daten erneut über die Leitung zu schicken – und das summiert sich sehr schnell. (Auch ansonsten war – und ist – der Drittcode von unglaublich vielen Speicheralloziierungen durchsetzt. Ein weiteres Beispiel besser auf getestete Komponenten zurückzugreifen und nicht das eckige Rad stets neu zu erfinden. Für Java gibt es verschiedene Projekte die neuronale Netze bereitstellen wie z.B. Joone oder nnwj.)

Um möglichst wenig am Code ändern zu müssen habe ich mich dann darauf beschränkt einen statischen Initializer zu bauen, der beim Laden der Klasse das Netzwerk initialisiert, und nicht bei jedem Durchlauf.

Ein paar JUnit Tests halfen mir, dass nach der Code Umstellung auch alles weiterhin so tat, wie es sollte, und ich den Änderungen vertrauen konnte. Auf einem Client sieht dann (dank der Einblicke der VisualVM von Java 6) der Speicherverbrauch auch vernünftig aus. Nach einem manuellen Garbage-Collector-Lauf rund 25MB auf dem Heap. Ausgezeichnet.

Gelernte Lektionen:

  • GridGain ist ein einfach zu verwendendes und dabei sehr flexibles Framework für Grid-Computing, Respekt!
  • die frei verfügbaren Programme und Tools von Sun sind Werkzeuge, die sich durchaus mit anderen kommerziellen Produkten messen können
  • VisualVM und der Netbeans Profiler lohnen einen Einsatz auch ohne konkreten Verdacht – manche Ergebnisse sind überraschend
  • Für GridGain Tasks (und Jobs) sollte man versuchen den Objektgraphen klein zu halten, und die jeweilligen Jobs möglichst zustandslos umzusetzen, sonst macht der Kommunikationsoverhead die Vorteile des Grid zunichte
  • Unit Tests!