Java SE 7 auf dem Weg zu mehr Performance und Möglichkeiten
Erst letzte Woche hat Kollege Rodewig im Rahmen unseres Trend Reports ein wenig abgelästert, nicht jeder Software-Entwickler müsse sich mit Java herumschlagen. Doch wir haben für Klaus und alle angesprochenen Java-Entwickler eine gute Nachricht: mit der seit Anfang Juli verfügbaren nächsten Version der Java-Entwicklungsumgebung mit der Versionsnummer 7 stehen auf einmal ganz neue Möglichkeiten zur Verfügung. Und das betrifft nicht nur kosmetische Veränderungen.
Eine der auffälligsten Verbesserungen – zumindest auf den zweiten Blick – betrifft die Leistungsfähigkeit von Java, vor allem auf Mehrkernprozessoren, die ja mittlerweile in nahezu sämtlichen modernen Rechnersystemen stecken und die Anwendungen erheblich beschleunigen können – wenn dies die Anwendung respektive das darunter liegende Programmiermodell mitmacht. Und genau hier stieß Java an seine Grenzen – bis jetzt. Denn mit Einführung von Java SE 7 lassen sich mithilfe von Threads nicht nur asynchrone Aufgaben ausführen, sondern ab sofort auf Basis von Thead Pools und verbesserten Synchronisationsmöglichkeiten das Ausführen paralleler Arbeiten erheblich beschleunigen.
Hierfür hat sich die Java-Community, die hinter Java 7 steht, für den Fork/Join-Ansatz entschieden, der endlich das Dilemma fehlender Task Manager beseitigt. So gibt es zwar schon seit fast zehn Jahren zahlreiche Tools und Klassen, die allesamt auf mehr Parallelität von Java-Anwendungen abzielten, aber das Vewalten mehrerer Threads mussten Java-Entwickler immer noch selbst in die Hand nehmen, was erfahrungsgemäß keine triviale Herausforderung darstellt.
Mit Fork/Join wird dieses Problem aus der Welt geschafft, indem die bereits vorhandenen ExecutorServices dergestalt erweitert wurden, dass Arbeitspakete ausgeführt werden können, die wiederum das Runnable Interface implementieren. Folge: Threads lassen sich verwalten und beliebig manipulieren, was andere Laufzeitumgebungen schon seit geraumer Zeit anbieten.
Zu Erinnerung: ein Modell für die Realisierung eines Fork/Join-Frameworks für Java existiert bereits seit rund elf Jahren, als sich ein gewisser Doug Lea Gedanken darum machte, wie Aufgaben in Java parallel verarbeitet werden könnten. Herausgekommen ist ein Devide-and-Conquer-Modell, das vorsieht, dass Aufgaben in sinnvolle Unteraufgaben geteilt werden können, die dann gleichzeitig erledigt werden können und am Schluss wieder zusammengeführt werden, um das richtige Ergebnis zu bekommen.
Genau daraus entstanden ist das Fork/Join-Framework von Java 7, das vor allem bei rechenintensiven Aufgaben ohne größere I/O-Aktivitäten eine sinnvolle Ergänzung zum bisherigen Java-Thread-Pooling darstellt. Dass hierbei eine ganze Menge Fingerspitzengefühl des Entwicklers notwendig ist, versteht sich fast von selbst. Denn mit zunehmender Parallelisierung des Java-Codes erhöht sich der Koordinationsaufwand zur Verwaltung der notwendigen Tasks, was bei einem schlecht dimensionierten Threadpool oder einer nicht-optimalen Granularität der Subtasks schell zum Hemmschuh werden kann. Daher ist ein Profiling der zu optimierenden Anwendung unerlässlich. Und wie das geht, sollten unsere aufmerksamen Stammleser wissen, oder?!
Tech-Interview: Mit Java-Extension einfach parallel programmieren
Die Kollegen vom SoftTalk-Blog hatten die Gelegenheit, ein Interview mit Patrick Viry zu führen, der eine Software-Firma in Paris betreibt. Er entwickelt dort mit seinem Team Erweiterungen für etablierte Progammiersprachen und fokussiert sich derzeit mit seinen Ateji PX Extension auf Java. Die Antworten fand ich so interessant, dass ich sie in Auszügen wiedergeben möchte. Das komplette Interview können Sie auf dem SoftTalk-Blog nachlesen.
Wie schafft es Ateji, die Parallelprogrammierung einfacher zu machen?
Nun, zunächst haben wir eine bestehende Sprache erweitert (nämlich Java), sodass es nicht notwendig ist, eine neue Sprache zu lernen (weitere sollen folgen). Dann haben wir auf Basis einer mathematischen Formel eine parallele Extension entwickelt, mit deren Hilfe sich parallele Ausdrücke mit einem einfachen Operator kombinieren lassen. Wenn Sie beispielsweise eine Funktion a und b parallel ablaufen lassen wollen, müssen Sie das lediglich als a || b in Ihren Quellcode einfügen.
Welche Programmiermodelle unterstützen Sie?
Dazu gehören eine ganze Menge:
- task-basierte Modelle wie Cilk
- daten-basierte Modelle wie OpenMP
- rekursive und spekulative parallele Konstrukte
- und einiges mehr.
Worin unterscheiden sich die Ateji-Extensions von anderen Erweiterungen, die es zum Zwecke der Parallelprogrammierung bereits gibt?
Nun, da gibt es eine Reihe von Unterschieden:
- Chapel, X10, Fortress, etc. adressieren den Bereich des High Performance Computing (HPC), sind also vor allem für Supercomputer ausgelegt. Ateji PX wurde für “einfache” Java-Entwickler geschrieben, die Anwendungen für herkömmliche Multicore-Server und -Cluster schreiben.
- Es handelt sich dabei im eine Spracherweiterung, die mit Standard-Java-Code kompatibel ist.
- Der einfach zu erlernende ||-Operand erlaubt es, diverse Parallelisierungs-Konstrukte mithilfe einer einzigen Sprache auszudrücken.
- Unsere Extension basiert auf einer erprobten, zuverlässigen mathematischen Formel, sodass wir genau vorhersagen können, was innerhalb des parallelen Konstrukts passieren wird. Das bedeutet auch, dass der Compiler mögliche Fehler wie Data Races relativ genau vorhersagen kann.
Wie reagieren Entwickler auf Ihre Java-Extension?
Nun, unsere Hauptaussage, dass mit Ateji PX die Parallelprogrammierung einfacher wird, ließ sich schon des öfteren bestätigen. So können Entwickler in der Regel innerhalb eines halben Tages ihr erstes parallel programmiertes Programm schreiben und zum Laufen bringen. Und ein großes Bankinstitut hat es tatsächlich geschafft, innerhalb von zwei Tagen ihre Java-Anwendung derart zu parallelisieren, dass sie hinterher fünf mal schneller lief als ohne den Parallelisierungsaufwand.
Weitere Informationen zur Ateji PX Extension für Java gibt es im zu zugehörigen PDF oder auf der Ateji PX Webseite. Es gibt auch eine Case Study, die zeigt, wie sich mit Hilfe von Ateji eine Server-Anwendung mit 16 Prozessorkernen um den Faktor 12,5 beschleunigen ließ.
Parallelisierungsgrad von .NET- und Java-Anwendungen testen
Zugegeben, ganz neu ist der Intel Concurrency Checker nicht . Mit diesem Tool lässt sich ziemlich einfach der Parallelisierungsgrad einer Anwendung messen – und zwar ganz egal, ob es sich dabei um ein C++-, .NET- oder Java-Programm handelt. Denn das Intel-Tool untersucht – wie so viele andere Software-Werkzeuge – nicht den Sourcecode des zu testenden Programms, sondern erstellt mithilfe der laufenden Anwendung und des daraus resultierenden Datenaufkommens eine Testdatei, in der bestimmte Parameter abgelegt werden. Anhand dieser Werte lässt sich dann der Parallelisierungsgrad der Anwendung ermitteln.
Das kann man einerseits selbst erledigen, indem man zunächst bestimmte Konstrukte parallelsiert, um den Multicore-Koeffizienten zu verbessern. Hierzu eignen sich ja beispielsweise Schleifenkonstrukte, die eine große Zahl an Daten in kurzer Zeit manipulieren. Sobald man testweise eine for-Schleife mit dem richtigen Befehl und dem dazu passenden Programmiermodell (wie zum Beispiel OpenMP) parallelisiert hat, kompiliert man die Anwendung erneut, startet den Concurrency Checker und betrachtet die so ermittelten Testergebnisse.
Das ist natürlich aufwändig und erfordert ein gewisses Maß an Paralleliserungs-Know-now. Einfacher geht es da mit dem zugehörigen Service, den Intel Software-Entwicklern kostenlos anbietet. Das einzige, was Sie hierfür benötigen, ist ein gültiger Account des Intel Software Partner Program. Sobald Sie Besitzer der notwendigen Login-Daten sind, können Sie den Testbericht, den der Concurrency Checker automatisch ausspuckt, auf den Intel-Server laden. Dieser wertet die Daten aus und stellt diese in grafischer Art und Weise am Bildschirm dar.
Falls die hierfür eingesetzten Tools Parallelisierungspotenzial innerhalb Ihrer Anwendung finden, bekommen Sie das mitgeteilt und Sie müssen sich selbst um das Aufspüren der möglichen Sourcecode-Stellen nicht kümmern. Was Sie allerdings schon wissen sollten, wie Sie die identifizierten Stellen mit entsprechenden Parallel-Konstrukten versehen. Und das wiederum erfordert ein wenig mehr als nur den Concurrency Checker. Aber wozu gibt es dieses Blog, oder?!
Falls Sie sich zu dem Thema ein Video angucken wollen, hilft ein Mausklick auf diesen Link. Oder Sie besuchen die Concurrency-Checker-Seite, die Intel speziell zu diesem Thema zusammen gezimmert hat.
Web-Tipp: Parallel programmieren in Java
Gerade in Zeiten der Endlosdiskussionen “Offene versus geschlossene Systeme” (also zum Beispiel Android versus iPhone) sollte man sich die offenen Systeme mal genauer ansehen, was sie denn in Sachen Multicore-Programmierung zu bieten haben. Bei der Recherche für diesen Blog-Eintrag bin ich dann prompt auf einen Online-Kurs von Sun gestoßen, in dem es zwar nicht sehr detailliert, aber dafür recht anschaulich und verständlich um das Thema “Concurrency” geht. Allerdings gibt es gleich zu Anfang einen Hinweis, der (a) verwirrend und (b) inhaltlich nicht ganz richtig ist:
The Java platform is designed from the ground up to support concurrent programming, with basic concurrency support in the Java programming language and the Java class libraries. Since version 5.0, the Java platform has also included high-level concurrency APIs.
Verwirrend daran ist die Tatsache, dass davon die Rede ist, Java sei von Hause aus für die Parallelprogrammierung konzipiert worden. Das stimmt natürlich nicht, was im zweiten Satz auch klargestellt wird. Und der Hinweis auf die grundlegende Unterstützung in Sachen Multithreading in Java macht es ebenfalls deutlich: Java bietet zwar Multicore-Support, der ist aber nicht trivial anzuwenden und erfordert sicherlich mehr Informationen als die Lektion “Concurrency”, die auf der Sun-Seite zur Verfügung gestellt wird.
Nichtsdestotrotz ist dieser kleine Workshop ein guter Einstieg in die Welt der Java-Multicore-Programmierung. Es wird anhand kurzer Codebeispiele gezeigt, wie sich …
- Threads erzeugen und starten lassen
- Threads per Sleep Messages() temporär anhalten lassen
- Threads unterbrechen lassen
- Threads anhalten lassen, damit sie auf einen anderen Thread warten.
Multicore-Fähigkeiten mit Spezialtool herausfinden
Im bayerischen Pilsting-Großköllnbach kennt man sich ziemlich gut aus mit der Archivierung von relationalen Datenbanken. Denn in Pilsting-Großköllnbach sitzt die Software-Schmiede CSP GmbH und Co. KG, die das Archivierungstool Chronos entwickelt hat. Diese Anwendung lagert nicht permanent benötigte Daten in ein Langzeitarchiv aus, um so die vorhandenen Datenbankserver kontinuierlich zu entlasten.
Das Extrahieren dieser Daten geschieht in Echtzeit, also während des laufenden Betriebs. Um eine möglichst reibungslose Archivierung zu garantieren, hat sich die CSP schon sehr früh auf das Multithreaden ihrer Anwendung konzentriert.
Das gesamte Konzept ist also auf parallele Datenverarbeitung ausgelegt. Es findet sogar eine doppelte Parallelisierung statt: Es kommt ein Mini-Cluster zum Einsatz, in dem einzelne Server bestimmte Aufgaben übernehmen. Und innerhalb dieser Server gibt es eine hohe Multithreading-Kultur, die eine skalierende Ausführung der Anwendung sicherstellt.
Chronos ist übrigens vollständig in Java programmiert und findet innerhalb der Java Runtime Environment statt, die standardmäßig mit Java-Threads auskommt. Auf diesem Weg können Java-Anwendungen mithilfe bestimmter Bibliotheken und Klassen parallelisiert werden. Für die komplette Verwaltung sämtlicher Threads hat CSP übrigens einen eigenen Threadmanager entwickelt, der im Hintergrund analog zu den Threading Building Blocks die einzelnen Tasks auf die vorhandenen Prozessoren und Threads verteilt. Für eine bestmögliche Auslastung der Ressourcen.
Wieso können sich die Entwickler von CSP aber so sicher sein, dass Chronos wirklich optimal auf Intel Multicore-Plattformen skaliert? Nun, um das herauszufinden, besorgte man sich in Pilsting-Großköllnbach das kostenlose Tool Concurrency Checker, mit dessen Hilfe festgestellt werden kann, ob eine Anwendung effizient auf Intel Multicore-Plattformen läuft.
Und die Entwickler der CSP GmbH waren ziemlich schnell davon überzeugt, dass der Concurrency Checker das richtige Tool für ihre Zwecke ist. Denn sie waren sehr zufrieden mit der Bedienbarkeit des Programms, da sie für einen ausführlichen Multicore-Test gerade mal einen halben Tag investieren mussten. Danach war den CSP-Programmierern klar, dass sich der Concurrency Checker für ihre Zwecke optimal einsetzen lässt. Und dass Chronos auch das hält, was es verspricht: eine optimale Ausnutzung der vorhandenen Systemressourcen, sprich der Prozessorkerne und Threads.


