Benchmarks werden unter intel core i5, Ubuntu
Ich vergleiche die Leistung von Collectors.counting
und Collectors.summingLong(x -> 1L)
. Hier ist der Maßstab:
Ich habe das Ergebnis, dass Collectors.counting
3 mal langsamer Collectors.summingLong
.
Also habe ich es mit -prof perfnorm
mit 25 Gabeln laufen lassen. Hier ist das Ergebnis:
Was mir aufgefallen ist:
branches
, instructions
, cycles
unterscheidet sich fast dreimal. Auch Cache-Operationen. Äste scheinen gut vorhergesagt, auch nicht zu viel Cache vermisst (nur meine Meinung).
Das Problem könnte also beim kompilierten Code liegen. Habe es mit -prof perfasm
(zu lange, um es hier zu setzen).
Im kompilierten Code habe ich folgendes bemerkt:
I. Collectors.summingLong
Montage
Wir haben 3 Schleifen, die durch das Array iterieren und zählen. Zuerst zählt nur ein Element
%Vor%Second zählt 4 Elemente für 1 Iteration (Ist diese Schleife entrolling?) und auch nach dem ersten.
%Vor%Und der dritte zählt den Rest der Elemente.
II . Collectors.counting
Assembly
Wir haben nur eine Schleife, die alle Elemente eins nach dem anderen zählt (nicht abgerollt). Außerdem haben wir die Boxen-Konvertierung in die Schleife des Ergebnisses des Zählens integriert. Auch scheinen wir die Boxen-Konvertierung innerhalb der Schleife nicht eingeplant zu haben.
%Vor% scheint Boxing der 1L
in Lambda e -> 1L
durchzuführen. Aber das ist nicht klar warum. Beim Ausführen der tatsächlichen Addition haben wir diesen Code:
Außerdem speichern wir das Ergebnis des Zählens im Stack mov %r10d,0x10(%rsp)
anstelle des Heaps wie im ersten Fall.
Wenn ich richtig verstanden habe, was vor sich geht, habe ich
FRAGE: Führt das Loop-Entrollen mit Box-Conversions zu dreimaliger Verlangsamung? Wenn ja, warum runtime runtime die Schleife nicht in der counting
-Fall?
Beachten Sie, dass Collectors.summingLong
2,5 mehr GC-Druck hat als Collectors.counting
. Das ist nicht ganz klar (ich konnte nur vermuten, dass wir Zwischenwerte im Stapel in Collectors.counting
speichern).
Ich habe die Assembly nicht angesehen oder analysiert, aber ein Blick auf die Quellen liefert bereits einige Informationen.
summingLong()
führt zu diesem Kollektor:
counting()
führt dazu:
Wie Sie sehen können, macht die counting()
Version noch einiges mehr:
op.apply(...)
auf
Da op
ist Long::sum
, das auf primitiven verwendet wird, gibt es eine Menge Boxen und Unboxing invoved.