Übersichtlicher und Performanter Code mit Scala
Auf der OOP hat die Firma dynatrace, welche sich auf die Analyse von Java-Performance spezialisiert hat, eine „Java Performance Challenge“ abgehalten: welcher von zwei dargestellten Stück Java-Code läuft schneller?
Als ich mir die Stücke Java-Code anschaute, überlegte ich mir, wie das funktionale Pendant aussehen würde, ob es lesbarer ist und ob das Performance-Problem dort auch auftauchen würde.
Zunächst zum ersten Stück Java-Code, das aus einer Applikation stammt, die zu langsam lief:
Der Code enthält zwei verschachtelte Schleifen, welche mittels eines
StringBuilder
eine Route erzeugen und eine Anzahl von Knoten
berechnen.
Wie sieht das funktional Pendant aus? Da das Original in Java ist, bietet sich Scala an:
Zunächst stellt man fest, dass der Scala-Code viel kürzer ausfällt.
Außerdem fällt auf, dass er keine for
-Schleifen enthält. Stattdessen
bevorzuge ich Operatoren wie zum Beispiel map
: das erste
map({routeNumber => ...})
liefert die Subrouten, das zweite
map({infoNumber => ...})
liefert die Länge einer Subroute und die
Subroute selbst. subRoutes
ist damit eine Liste von Paaren, dessen
erstes Element die Länge der Subroute und dessen zweites Element die
Subroute selbst ist.
Um die Ergebnisroute route
zu berechnen, bilde ich mit map
die
subRoutes
auf ihr zweites Element ab (x._2
) und verbinde sie zu
einem einzigen String mit mkString
, das die Subrouten mit dem String
"\n|"
verbindet. Die Anzahl der Knoten totalNodeCount
ist die
Summe der ersten Paar-Elemente.
Was ist mit dem Peformance-Problem in der Scala-Version? Die Stelle,
welche im Java-Programm das Performance-Problem erzeugte, taucht im
Scala-Programm nicht auf: das Aneinanderhängen der Routenelemente wird
von mkString("\n|")
erledigt.
Die Scala-Version läuft etwa 50 Mal schneller als das erste Stück Java-Code und etwa halb so schnell wie das zweit Stück Java-Code. Für ein Stück Code, das übersichtlicher ist und das das ursprüngliche Performance-Problem nicht aufzeigt, finde ich das gut genug.
Geht es schneller in Scala? Wenn der Code auf einem kritischen Pfad
liegt, kann man auch eine schnellere Scala-Version schreiben. Diese
kann dann fast so aussehen wie die Java-Version und ist dann fast so
schnell. Wenn das immer noch nicht reicht, kann man aus Scala heraus
direkt Java-Code aufrufen (System.err.print
ist zum Beispiel
Java-Code).
Mit Scala erhält man also für das ursprüngliche Problem übersichtlichen Code, der auf Anhieb schnell genug ist.
Das zweite Stück Java-Code ist die schnellere Version und sieht wie folgt aus:
Der Unterschied zum ersten Stück Java-Code ist auf den ersten Blick
kaum auszumachen: statt strRouteList.toString().isEmpty()
prüft das
zweite Stück Java-Code die Variable totalNodeCount == 1
. Damit
vermeidet es, dass ständig ein (potentieller langer) String erst
erzeugt wird und dann nur geprüft wird, ob er überhaupt etwas enthält.
Das erste Stück Java-Code läuft daher etwa 100 Mal langsamer als das
das zweite Stück Java-Code.
Hier kann man den Scala-Code zu diesem Beitrag herunterladen.
Zum Schluss möchte ich mich bei dynatrace bedanken, welche mir die Idee zu diesem Blogbeitrag lieferten und mir freundlicherweise völlig unbürokratisch den Java-Code zur Verfügung stellten. Auf ihrem englischen Blog About:Performance finden Interessierte mehr über Performance und Skalierbarkeit.