Was denken Sie, sind diese beiden Varianten zur Überprüfung von Bedingungen innerhalb einer Schleife hinsichtlich der Leistung äquivalent?
if a > b && c*2 > d {
....
}
// und
if a d {
....
}
Alles begann mit einer "Aufwärmübung für das Gehirn"; ich sollte ein Beispiel für die optimale Suche nach der größten geraden Zahl in einem Array von Ganzzahlen [-x….x] geben. Mich interessierte, wie viel höher die Leistung wäre, wenn man zur Bestimmung, ob eine Zahl gerade ist oder nicht, die logische Multiplikation mit 1 verwenden würde.
//у четных чисел последний бит всегда равен 0
value & 1 == 0
//vs классический метод
value % 2 == 0
Meine Programmiererfahrung in Go ist nicht sehr groß, etwas mehr als anderthalb Jahre. Ich habe es oft genutzt, aber eher zu utilitaristischen Zwecken (vielleicht abgesehen von einem Projekt, das mit einem hochbelasteten HTTP-Service zusammenhängt), deshalb habe ich genau damit angefangen. Öffnen wir GoLand und schreiben einen einfachen Test.
package main
import (
"fmt"
"log"
"math"
"math/rand"
"time"
)
const size = 100000000 //math.MaxInt32*2
type Result struct {
Name string
Duration time.Duration
Value int32
}
func main() {
log.Println("initial array capacity: " + fmt.Sprint(size))
var maxValue int32
// Wir werden den Zahlenbereich von minimal bis maximal variieren. Je kleiner der Bereich, desto mehr Prozessorzeit wird für den Vergleich der aktuellen Zahl mit der zuvor gefundenen benötigt.
for maxValue = 128; maxValue Wir erhalten ein Ergebnis, das zeigt, dass je höher der Schwellenwert, desto häufiger die Schwankungen in Bezug auf die Leistung auftreten.
Vergleichen Siemaximale Schwelle: 128
maxEvenDividing Ergebnis: 126 Dauer 116.0067ms
maxEvenConjunction Ergebnis: 126 Dauer 116.0066ms
max threshold: 16384
maxEvenDividing result: 16382 duration 115.0066ms
maxEvenConjunction result: 16382 duration 111.0064ms
......
max threshold: 8388608
maxEvenDividing result: 8388606 duration 109.0063ms
maxEvenConjunction Ergebnis: 8388606 Dauer 109,0062ms
max Schwelle: 16777216
maxEvenDividing Ergebnis: 16777214 Dauer 108,0062ms
maxEvenConjunction Ergebnis: 16777214 Dauer 109,0062ms
max Schwelle: 33554432
maxEvenDividing Ergebnis: 33554430 Dauer 114,0066ms
maxEvenConjunction Ergebnis: 33554430 Dauer 110,0063ms
max Schwelle: 67108864
maxEvenDividing Ergebnis: 67108860 Dauer 111,0064ms
maxEvenConjunction Ergebnis: 67108860 Dauer 109,0062ms
max Schwelle: 134217728
maxEvenDividing Ergebnis: 134217726 Dauer 108,0062ms
maxEvenConjunction Ergebnis: 134217726 Dauer 109,0063ms
max Schwelle: 268435456
maxEvenDividing Ergebnis: 268435446 Dauer 111,0063ms
maxEvenConjunction Ergebnis: 268435446 Dauer 110,0063ms
Es ist klar, dass wir in diesem Fall für unterschiedliche Schwellenwerte unterschiedliche Testdatensätze haben. Die CPU-Auslastung (auf meinem Laptop i5-2540M) schwankt zwischen 20 und 30 %. Der Speicher, der von der aus GoLand gestarteten Anwendung belegt wird, liegt im Durchschnitt bei etwa 813 MB — das beeinflusst auch die Zuverlässigkeit des Ergebnisses. Es sollte eine Speicherung der Testdatensätze auf der Festplatte implementiert werden und alle Tests für jeden Schwellenwert isoliert durchgeführt werden.
Und während ich darüber nachdenke, wie ich das mit minimalen Kosten umsetzen kann, korrigiere ich mechanisch die Bedingungsprüfung.
if value > current && value&1 == 0 {
current = value
}
auf
if value <= current {
continue;
}
if value&1 == 0 {
current = value
}
Ich starte die Tests erneut... und verstehe nichts mehr 🙂
Die benötigte Zeit für die Ausführung beginnt nicht mehr nur um Prozentsätze zu schwanken, sondern um 10..15 %. Ich schreibe schnell noch 2 Tests:
func maxEvenDividing2(name string, arr []int32) Result {
start := time.Now()
var current int32 = math.MinInt32
for _, value := range arr {
if value <= current {
continue
}
if value%2 == 0 {
current = value
}
}
duration := time.Since(start)
result := Result{name, duration, current}
return result
}
func maxEvenConjunction2(name string, arr []int32) Result {
start := time.Now()
var current int32 = math.MinInt32
for _, value := range arr {
if value <= current {
continue
}
if value&1 == 0 {
current = value
}
}
duration := time.Since(start)
result := Result{name, duration, current}
return result
}
Ich starte und erhalte so ein Bild:anfängliche Array-Kapazität: 100000000
maximale Schwelle: 128
maxEvenDividing Ergebnis: 126 Dauer 116,0066ms
maxEvenDividing2 Ergebnis: 126 Dauer 79,0045ms
maxEvenConjunction Ergebnis: 126 Dauer 114,0065ms
maxEvenConjunction2 Ergebnis: 126 Dauer 83,0048ms
max Schwelle: 256
maxEvenDividing Ergebnis: 254 Dauer 111,0063ms
maxEvenDividing2 Ergebnis: 254 Dauer 77,0044ms
maxEvenConjunction Ergebnis: 254 Dauer 110,0063ms
maxEvenConjunction2 Ergebnis: 254 Dauer 80,0046ms
max Schwelle: 512
maxEvenDividing Ergebnis: 510 Dauer 114,0066ms
maxEvenDividing2 Ergebnis: 510 Dauer 80,0045ms
maxEvenConjunction Ergebnis: 510 Dauer 110,0063ms
maxEvenConjunction2 Ergebnis: 510 Dauer 80,0046ms
max Schwelle: 1024
maxEvenDividing Ergebnis: 1022 Dauer 109,0063ms
maxEvenDividing2 Ergebnis: 1022 Dauer 77,0044ms
maxEvenConjunction Ergebnis: 1022 Dauer 111,0063ms
maxEvenConjunction2 Ergebnis: 1022 Dauer 81,0047ms
max Schwelle: 2048
maxEvenDividing Ergebnis: 2046 Dauer 114,0065ms
maxEvenDividing2 Ergebnis: 2046 Dauer 79,0045ms
maxEvenConjunction Ergebnis: 2046 Dauer 113,0065ms
maxEvenConjunction2 Ergebnis: 2046 Dauer 81.0046ms
max Schwellenwert: 4096
maxEvenDividing Ergebnis: 4094 Dauer 114.0065ms
maxEvenDividing2 Ergebnis: 4094 Dauer 80.0046ms
maxEvenConjunction Ergebnis: 4094 Dauer 111.0063ms
maxEvenConjunction2 Ergebnis: 4094 Dauer 78.0045ms
max Schwellenwert: 8192
maxEvenDividing Ergebnis: 8190 Dauer 107.0062ms
maxEvenDividing2 Ergebnis: 8190 Dauer 77.0044ms
maxEvenConjunction Ergebnis: 8190 Dauer 111.0063ms
maxEvenConjunction2 Ergebnis: 8190 Dauer 77.0044ms
max threshold: 16384
maxEvenDividing Ergebnis: 16382 Dauer 109.0063ms
maxEvenDividing2 Ergebnis: 16382 Dauer 77.0044ms
maxEvenConjunction Ergebnis: 16382 Dauer 108.0062ms
maxEvenConjunction2 Ergebnis: 16382 Dauer 77.0044ms
max Schwellenwert: 32768
maxEvenDividing Ergebnis: 32766 Dauer 112.0064ms
maxEvenDividing2 Ergebnis: 32766 Dauer 77.0044ms
maxEvenConjunction Ergebnis: 32766 Dauer 109.0062ms
maxEvenConjunction2 Ergebnis: 32766 Dauer 78.0045ms
max Schwellenwert: 65536
maxEvenDividing Ergebnis: 65534 Dauer 109.0062ms
maxEvenDividing2 Ergebnis: 65534 Dauer 75.0043ms
maxEvenConjunction Ergebnis: 65534 Dauer 109.0063ms
maxEvenConjunction2 Ergebnis: 65534 Dauer 79.0045ms
max Schwellenwert: 131072
maxEvenDividing Ergebnis: 131070 Dauer 108.0061ms
maxEvenDividing2 Ergebnis: 131070 Dauer 76.0044ms
maxEvenConjunction Ergebnis: 131070 Dauer 110.0063ms
maxEvenConjunction2 Ergebnis: 131070 Dauer 80.0046ms
max Schwellenwert: 262144
maxEvenDividing Ergebnis: 262142 Dauer 110.0063ms
maxEvenDividing2 Ergebnis: 262142 Dauer 76.0044ms
maxEvenConjunction Ergebnis: 262142 Dauer 107.0061ms
maxEvenConjunction2 Ergebnis: 262142 Dauer 78.0044ms
max Schwellenwert: 524288
maxEvenDividing Ergebnis: 524286 Dauer 109.0062ms
maxEvenDividing2 Ergebnis: 524286 Dauer 78.0045ms
maxEvenConjunction Ergebnis: 524286 Dauer 109.0062ms
maxEvenConjunction2 Ergebnis: 524286 Dauer 80.0046ms
max Schwellenwert: 1048576
maxEvenDividing Ergebnis: 1048574 Dauer 109.0063ms
maxEvenDividing2 Ergebnis: 1048574 Dauer 80.0045ms
maxEvenConjunction Ergebnis: 1048574 Dauer 114.0066ms
maxEvenConjunction2 Ergebnis: 1048574 Dauer 78.0044ms
max Schwellenwert: 2097152
maxEvenDividing Ergebnis: 2097150 Dauer 111.0064ms
maxEvenDividing2 Ergebnis: 2097150 Dauer 79.0045ms
maxEvenConjunction Ergebnis: 2097150 Dauer 112.0064ms
maxEvenConjunction2 Ergebnis: 2097150 Dauer 77.0044ms
max Schwellenwert: 4194304
maxEvenDividing Ergebnis: 4194302 Dauer 111.0063ms
maxEvenDividing2 Ergebnis: 4194302 Dauer 78.0045ms
maxEvenConjunction Ergebnis: 4194302 Dauer 111.0063ms
maxEvenConjunction2 Ergebnis: 4194302 Dauer 77.0044ms
max threshold: 8388608
maxEvenDividing Ergebnis: 8388606 Dauer 109.0062ms
maxEvenDividing2 Ergebnis: 8388606 Dauer 78.0045ms
maxEvenConjunction Ergebnis: 8388606 Dauer 114.0065ms
maxEvenConjunction2 Ergebnis: 8388606 Dauer 78.0045ms
max Schwelle: 16777216
maxEvenDividing Ergebnis: 16777214 Dauer 109.0062ms
maxEvenDividing2 Ergebnis: 16777214 Dauer 77.0044ms
maxEvenConjunction Ergebnis: 16777214 Dauer 109.0063ms
maxEvenConjunction2 Ergebnis: 16777214 Dauer 77.0044ms
max Schwelle: 33554432
maxEvenDividing Ergebnis: 33554430 Dauer 113.0065ms
maxEvenDividing2 Ergebnis: 33554430 Dauer 78.0045ms
maxEvenConjunction Ergebnis: 33554430 Dauer 110,0063ms
maxEvenConjunction2 Ergebnis: 33554430 Dauer 80.0045ms
max Schwelle: 67108864
maxEvenDividing Ergebnis: 67108860 Dauer 112.0064ms
maxEvenDividing2 Ergebnis: 67108860 Dauer 77.0044ms
maxEvenConjunction Ergebnis: 67108860 Dauer 112.0064ms
maxEvenConjunction2 Ergebnis: 67108860 Dauer 80.0046ms
max Schwelle: 134217728
maxEvenDividing Ergebnis: 134217726 Dauer 109.0063ms
maxEvenDividing2 Ergebnis: 134217726 Dauer 78.0044ms
maxEvenConjunction Ergebnis: 134217726 Dauer 114.0065ms
maxEvenConjunction2 Ergebnis: 134217726 Dauer 81.0047ms
max Schwelle: 268435456
maxEvenDividing Ergebnis: 268435446 Dauer 111.0064ms
maxEvenDividing2 Ergebnis: 268435446 Dauer 79.0045ms
maxEvenConjunction Ergebnis: 268435446 Dauer 114.0065ms
maxEvenConjunction2 Ergebnis: 268435446 Dauer 79.0045ms
max Schwellenwert: 536870912
maxEvenDividing Ergebnis: 536870910 Dauer 107.0062ms
maxEvenDividing2 Ergebnis: 536870910 Dauer 76.0043ms
maxEvenConjunction Ergebnis: 536870910 Dauer 109,0062 ms
maxEvenConjunction2 Ergebnis: 536870910 Dauer 80,0046 ms
Eine klare Erklärung, warum der Go-Compiler den Code nicht optimiert und immer die zweite Bedingung prüft, selbst wenn die erste falsch ist – habe ich nicht gefunden. Vielleicht habe ich einfach einen 'Blenden'-Effekt und sehe einen offensichtlichen Fehler nicht? Oder muss man dem Compiler besondere Anweisungen geben? Ich würde mich über hilfreiche Kommentare freuen.
PS: Ja, aus Interesse habe ich ähnliche Tests auf Java 5 und Java 7/8 durchgeführt – alles klar, die Ausführungszeiten sind gleich.
Quelle: habr.com
