Groovy: Ist for..in wesentlich schneller als .each?

8

Ich bin gespannt, ob for..in aus Leistungsgründen der .each vorgezogen werden sollte.

    
Alexander Suraphel 05.06.2015, 08:38
quelle

2 Antworten

4

For .. in ist Teil der Standardsprachflusskontrolle.

Stattdessen ruft each eine Schließung mit einem zusätzlichen Overhead auf.

.each {...} ist Syntaxzucker, der dem Methodenaufruf .each({...})

entspricht

Außerdem können Sie wegen der Tatsache, dass es sich um eine Schließung handelt, innerhalb von each code block die Anweisungen break und continue nicht verwenden, um die Schleife zu steuern.

Ссылка

Aktualisierter Benchmark Java 1.8.0_45 Groovy 2.4.3:

  • 3327981 jeweils {}
  • 320949 für () {

Hier ist ein weiterer Benchmark mit 100000 Iterationen:

%Vor%

Ergebnisse:

  • 261062715 jeder {}
  • 64518703 für () {}
frhack 05.06.2015, 09:09
quelle
4

Lassen Sie uns einen theoretischen Blick darauf werfen, was für Anrufe dynamisch erledigt werden und welche Anrufe direkter mit Java-Logik erledigt werden (ich werde diese statischen Anrufe anrufen).

Im Falle von for-in arbeitet Groovy auf einem Iterator, um es zu bekommen, wir haben einen dynamischen Aufruf von iterator (). Wenn ich mich nicht irre, werden die Aufrufe hasNext und next mit der normalen Aufruflogik der Java-Methode ausgeführt. Somit haben wir für jede Iteration nur 2 statische Aufrufe. Je nach Benchmark ist zu beachten, dass dieser erste Aufruf von iterator () zu ernsthaften Initialisierungszeiten führen kann, da dies das Meta-Klassensystem initalisieren kann und das einen Moment dauert.

Im Fall von each haben wir sowohl den dynamischen Aufruf an sich selbst als auch die Objekterstellung für den geöffneten Block (Instanz von Closure). jeder (Closure) wird dann auch iterator () aufrufen, aber nicht zwischengespeichert ... also alle einmaligen Kosten. Während der Schleife erfolgt die Ausführung von hasNext und next mithilfe von Java-Logik, die zwei statische Aufrufe durchführt. Der Aufruf der Closure-Instanz erfolgt mit Java-Standardlogik für den Methodenaufruf, der dann doCall mithilfe eines dynamischen Aufrufs aufruft.

Um es zusammenzufassen, pro% iteration verwendet for-in nur 2 statische Aufrufe, während each 3 statische und 1 dynamische Aufrufe hat. Der dynamische Aufruf ist viel langsamer als mehrere statische Aufrufe und viel schwieriger für die JVM zu optimieren, wodurch das Timing dominiert wird. Als Folge davon sollte each immer langsamer sein, solange der offene Block den dynamischen Aufruf benötigt.

Wegen der komplizierten Logik für Closure # Call ist es schwierig, den dynamischen Call Away zu optimieren. Und das ist ärgerlich, weil es nicht wirklich benötigt wird und entfernt wird, sobald wir einen Workaround finden. Sollte uns das jemals gelingen, wäre each vielleicht noch langsamer, aber es ist eine viel schwierigere Sache, da Bytecode-Größen und Aufrufprofile hier eine Rolle spielen. In der Theorie könnten sie dann gleich sein (Ignorieren der Initialisierungszeit), aber die JVM hat viel mehr zu tun. Dasselbe gilt natürlich auch für die strombasierte Lambda-Verarbeitung in Java8,

    
blackdrag 06.06.2015 06:18
quelle

Tags und Links