Blog

Interview mit Dr. Heinz Kabutz: Concurrency, funktionale Programmierung, Jigsaw – Ist Java fit für die Zukunft?

12 Feb 2018

Extreme Java Camp

Ist Java den aktuellen Herausforderungen der IT gewachsen? Was bedeutet die Renaissance des funktionalen Programmierens für das objektorientierte Paradigma? In welche Richtung sollte sich Java weiterentwickeln? Antworten gibt Dr. Heinz Kabutz, Java-Performance-Experte und Trainer auf dem Extreme Java Camp, im Interview.

Nebenläufigkeit in Java: Ist Java schnell genug?

JAXenter: Wir wollen uns ein wenig über Java unterhalten und darüber, wie die Programmiersprache den aktuellen Anforderungen der IT gerecht wird. Blicken wir dafür zunächst ein wenig zurück: Als Java 1995 Furore machte, stand die Sprache ja nicht gerade im Ruf, sonderlich schnell zu sein. Seither ist viel Zeit vergangen – und etliche Optimierungen sind passiert. Wie würdest du die Performance von Java heute einschätzen, vor allem im Vergleich zu Newcomern wie Google Go?

Dr. Heinz Kabutz: Ich vergleiche die Performance von Programmcode gerne mit Steuern. Als ich nach Griechenland gezogen bin, habe ich das aus strategischen Gründen getan. Meine Frau und Kinder sind griechisch. Nach EU-Recht musste Griechenland mir erlauben, trotz meines südafrikanischen Passes hier zu leben. 2007 kam dann meine erste griechische Steuererklärung, und ich war schockiert: Ich hatte ja keine Ahnung, wie wenig Steuern die Leute in Griechenland bezahlten.

Das war vor der Krise, die Gesetze haben sich mittlerweile geändert. Wenn damals unser einziger Grund, nach Griechenland zu ziehen, die niedrigen Steuern gewesen wären, würden wir uns jetzt ein anderes Zuhause suchen. Doch Griechenland war für uns ein strategischer Schachzug.

Wir können fast jede Sprache schnell laufen lassen. Doch die Kosten für die Entwicklung und die laufende Wartung sind viel wichtiger.

Auf die gleiche Weise müssen wir strategische Entscheidungen darüber treffen, welche Programmiersprache wir verwenden möchten. Benchmarks lügen. Go hat möglicherweise einen langsamen Regex-Parser, aber in deinem System musst du dieses Feature vielleicht nur in 0,01% der Zeit verwenden. Java startet einen neuen Thread auf deiner Maschine möglicherweise langsam, aber das könnte auch an deinem Betriebssystem liegen.

Die Fragen, die wir uns eigentlich stellen sollten, sind ganz andere: Wie einfach ist es, gute Entwickler in der XYZ-Umgebung zu finden? Welche Unternehmen unterstützen die weiteren Optimierungen der Infrastruktur? Ist es „schnell genug“?

Wir können fast jede Sprache schnell laufen lassen. Ich habe zum Beispiel ein Fahrzeugverfolgungssystem in Java in weniger als 100 KB programmiert. Doch die Kosten für die Entwicklung und die laufende Wartung sind viel wichtiger.

Ich möchte nicht zu einer Sprache zurückkehren, die keine automatische Speicherverwaltung hat. Ende 2016 habe ich mir einmal Swift angeschaut. Die Sprachstruktur ist schön und vermeidet viele Bugs, die wir in Java regelmäßig haben. Aber dann habe ich Swifts Speichermanagement gesehen und hörte auf zu lesen. Wenn ich programmiere, möchte ich nicht darüber nachdenken, wann zirkuläre Referenzen ein Speicherleck erzeugen könnten.

JAXenter: Eine der wichtigsten Entwicklungen in den letzten Jahren ist sicherlich das Aufkommen von Multikern-Prozessoren, die heute ja allgegenwärtig sind. Will man das Letzte aus seiner Anwendung herausholen, muss man sich aktiv um Concurrency kümmern. Wie gut ist Java, das ja eher eine Hardware-ferne Sprache ist, für nebenläufiges Programmieren ausgerüstet?

Dr. Heinz Kabutz: Zum Glück haben wir Nebenläufigkeit schon auf Chip-Level. Das macht unsere Systeme schneller und paralleler, ohne dass wir unsere Programmierpraktiken ändern müssen. Doch um die Vorteile von Multi-Core-Prozessoren voll auszunutzen, müssen wir unseren Code entsprechend anpassen. Wir wollen ja die volle Leistung unserer verfügbaren Hardware nutzen.

In Java ist dies mit dem Fork / Join-Framework möglich. Das ist kein perfektes System, da das Amdahlsche Gesetz bei der Zusammenführung der Ergebnisse in die Quere kommt. Die letzten Aufgaben werden auf einem einzelnen Kern ausgeführt. Aber wir können mehrere Kerne nutzen, ohne viel von unserem Code zu ändern. Alles, was wir tun müssen, ist, unsere Streams zur parallelisieren. Es gibt noch weitere Techniken, die dabei helfen, unsere verfügbare Hardware voll auszulasten. Fork / Join und ManagedBlocker fallen mir ebenso ein wie eine nicht-blockierende Queue.

JAXenter: Wie viel Performance verschenkt man eigentlich, wenn man sich nicht explizit um Concurrency kümmert?

Dr. Heinz Kabutz: Das hängt sehr von der Anwendung ab, die man schreibt, und von der Hardware, auf der man arbeitet. Moderne CPUs parallelisieren bereits sehr viel, ohne dass wir etwas Besonderes tun müssen. Hardware verwendet Techniken wie Pipelining, spekulative Ausführung und Hyperthreading. Die modernen Cache-Architekturen reduzieren zudem teure Zugriffe auf den Hauptspeicher. Es ist gut, dies in unserem Systemdesign zu berücksichtigen.

Moderne CPUs parallelisieren bereits sehr viel, ohne dass wir etwas Besonderes tun müssen.

Nehmen wir einmal an, wir müssen eine große Matrixmultiplikation durchführen. Wir könnten die Berechnung auf einmal machen. Aber wenn die Matrix nicht in den Level-2-Cache passt, werden wir viele Cache-Misses haben. Das wird uns ausbremsen. Es wäre dann schneller, die Multiplikation in kleinere Stücke aufzuteilen und dann das Ergebnis zu kombinieren. Martin Thompson nennt das „mechanical sympathy“.

Manchmal scheinen vermeintliche Leistungsoptimierungen die Dinge noch schlimmer zu machen. Auf einer 36-Core-Maschine würde beispielsweise eine einzelne Instanz von LongAdder ungefähr 36 KB Speicher belegen. Und dies, um 8 Bytes an Information zu speichern. Das Aktualisieren eines LongAddersaus mehreren Threads ist zwar super schnell. Aber wenn die Speicherkapazität knapp ist, sollten wir das nur dort machen, wo wir Zugriffskonflikte haben. Noch besser ist es, Threads oder den Stapel zu begrenzen, um solche Konflikte zu vermeiden.

Java und die funktionale Programmierung

JAXenter: Die Wichtigkeit des nebenläufigen Programmierens hat zu einer Renaissance eines Programmierparadigmas geführt, das viele bereits abgeschrieben hatten: die funktionale Programmierung. Ist man mit dem objektorientierten Stil denn tatsächlich so im Nachteil, wenn es um Concurrency geht?

Dr. Heinz Kabutz: Lisp oder Scheme sollte unsere erste Programmiersprache sein. Leider habe ich mit dem strukturellen Paradigma begonnen. Der nächste Schritt war die Objektorientierung. Erst dann habe ich gelernt, funktional zu programmieren. Das hat dann aber die Art und Weise verändert, wie ich in allen anderen Paradigmen programmiert habe. Dabei musste ich viele schlechte Gewohnheiten ablegen. Ich wünschte, ich hätte gleich mit Scheme begonnen.

Funktional ist die reinste Art der Programmierung, da sie Seiteneffekte vermeidet. Es gibt dabei keinen gemeinsamen, veränderbaren Speicher. Die Denkweise des Funktionalen lässt sich auf viele Sprachen anwenden, nicht nur auf Lisp. Man kann das funktionale Paradigma etwa auch mit Java, JavaScript oder PHP umsetzen.

Die Zukunft von Java

JAXenter: Wie schätzt du Java 9 mit seinem Projekt Jigsaw ein? Ist das ein wichtiges Release für die Zukunfsfähigkeit von Java? Oder ist Jigsaw  nur für Spezialisten interessant?

Dr. Heinz Kabutz: Jigsaw ist sehr wichtig für die Zukunft von Java. Selbst in frühen Versionen von Java war der Klassenpfad ein Durcheinander. Die Kosten für die Suche nach Klassen waren linear: Wenn der Klassenpfad länger wurde, stiegen auch die Suchkosten. Jigsaw adressiert dieses Problem und schließt Sicherheitslücken, die wir in der Vergangenheit hatten. Mit Jigsaw können wir Klassen innerhalb eines Moduls verfügbar machen und den Zugriff von außen einschränken. Zudem ist die Performance der Synchronisierung in Java 9 gestiegen.

Jigsaw ist sehr wichtig für die Zukunft von Java.

JAXenter: Blicken wir in die Zukunft. Wohin sollte sich Java deiner Meinung nach hin entwickeln? Welche Neuerungen wünscht du dir in den kommenden Versionen, Java 10, 11, 12…?

Dr. Heinz Kabutz: Wir müssen an den Garbage-Kollektoren arbeiten, um Stop-the-World-Pausen weiter zu reduzieren. ZGC und Shenandoah sehen in dieser Hinsicht  recht vielversprechend aus. Die AOT-Kompilierung sollte Java außerdem viel schneller starten lassen. Wir brauchen mehr eingebaute Parallelität in Java. Zum Beispiel könnte BigInteger multiply () die Fork/Join-Funktionalität nutzen, um die Operationen auf allen Kernen auszuführen.

JAXenter: Vielen Dank für dieses Interview!

 

Hartmut Schlosser

Hartmut Schlosser ist Redakteur und Online-Koordinator bei Software & Support Media. Seine Spezialgebiete liegen bei Java-Enterprise-Technologien, JavaFX, Eclipse und DevOps. Vor seiner Tätigkeit bei S & S Media studierte er Musik, Informatik, französische Philologie und Ethnologie.

 

 

 

 

Keine Infos mehr verpassen!