Glauben Sie, dass diese beiden Varianten zur Bedingungsprüfung in einer Schleife hinsichtlich der Leistung äquivalent sind?
if a > b && c*2 > d {
....
}
// und
if a d {
....
}
Alles begann mit einem „Gedankenwärmer“, ich sollte ein Beispiel für die optimale Suche nach der größten geraden Zahl in einem Array von ganzen Zahlen [-x….x] geben. Es interessierte mich, wie viel höher die Leistung wäre, wenn man zur Feststellung, ob eine Zahl gerade ist oder nicht, logische Multiplikation mit 1 verwendet.
//у четных чисел последний бит всегда равен 0
value & 1 == 0
//vs классический метод
value % 2 == 0
Meine Programmiererfahrung mit Go ist nicht sehr groß, etwas mehr als anderthalb Jahre, obwohl ich es oft verwendet habe, jedoch hauptsächlich zu nützlichen Zwecken (außer vielleicht bei einem Projekt, das mit einem hochbelasteten HTTP-Dienst verbunden war), weshalb ich genau damit begann. Lassen Sie uns GoLand öffnen und einen einfachen Test schreiben.
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("anfängliche Array-Kapazität: " + fmt.Sprint(size))
var maxValue int32
// Wir variieren den Zahlenbereich
// von minimal bis maximal. Je kleiner der Bereich, desto mehr
// Prozessorzeit wird für den Vergleich der aktuellen Zahl
// mit der zuvor gefundenen benötigt und umgekehrt
for maxValue = 128; maxValue Das Ergebnis zeigt, dass mit einem höheren Threshold die Schwankungen in der Performance häufiger auftreten.
Vergleichen SieMaximaler Schwellenwert: 128
maxEvenDividing Ergebnis: 126 Dauer 116,0067 ms
maxEvenConjunction Ergebnis: 126 Dauer 116,0066 ms
max Threshold: 16384
maxEvenDividing Ergebnis: 16382 Dauer 115.0066 ms
maxEvenConjunction Ergebnis: 16382 Dauer 111.0064 ms
......
max Threshold: 8388608
maxEvenDividing Ergebnis: 8388606 Dauer 109.0063 ms
maxEvenConjunction Ergebnis: 8388606 Dauer 109.0062 ms
max Threshold: 16777216
maxEvenDividing Ergebnis: 16777214 Dauer 108.0062 ms
maxEvenConjunction Ergebnis: 16777214 Dauer 109.0062 ms
max Threshold: 33554432
maxEvenDividing Ergebnis: 33554430 Dauer 114.0066 ms
maxEvenConjunction Ergebnis: 33554430 Dauer 110.0063 ms
max Threshold: 67108864
maxEvenDividing Ergebnis: 67108860 Dauer 111.0064 ms
maxEvenConjunction Ergebnis: 67108860 Dauer 109.0062 ms
max Threshold: 134217728
maxEvenDividing Ergebnis: 134217726 Dauer 108.0062 ms
maxEvenConjunction Ergebnis: 134217726 Dauer 109.0063 ms
max Threshold: 268435456
maxEvenDividing Ergebnis: 268435446 Dauer 111.0063 ms
maxEvenConjunction Ergebnis: 268435446 Dauer 110.0063 ms
Es ist klar, dass wir bei unterschiedlichen Thresholds verschiedene Testdatensätze haben. Die CPU-Auslastung (auf meinem Laptop i5-2540M) schwankt etwa zwischen 20 und 30 %, und der Speicher, den die Anwendung benötigt, die aus GoLand gestartet wurde, liegt im Durchschnitt bei ca. 813 MB. Das beeinflusst ebenfalls die Genauigkeit der Ergebnisse. Es sollte implementiert werden, dass Testsets auf der Festplatte gespeichert und alle Tests für jeden Threshold isoliert voneinander durchgeführt werden.
Während ich darüber nachdenke, wie ich das alles mit minimalen Kosten umsetzen kann, korrigiere ich automatisch die Bedingungsüberprüfung.
if value > current && value&1 == 0 {
current = value
}
findet man
if value <= current {
continue;
}
if value&1 == 0 {
current = value
}
Ich starte die Tests erneut … und höre auf, irgendetwas zu verstehen 🙂
Die benötigte Zeit für die Ausführung beginnt sich nicht nur um Prozentsätze, sondern um 10..15% zu unterscheiden. Ich füge schnell noch 2 Tests hinzu:
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 folgendes Bild:initiale Array-Kapazität: 100000000
Maximaler Schwellenwert: 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
maximale 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
maximale 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 Schwelle: 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 Schwelle: 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 Schwelle: 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 Schwelle: 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 Schwelle: 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 Schwelle: 262144
maxEvenDividing Ergebnis: 262142 Dauer 110,0063 ms
maxEvenDividing2 Ergebnis: 262142 Dauer 76,0044 ms
maxEvenConjunction Ergebnis: 262142 Dauer 107,0061 ms
maxEvenConjunction2 Ergebnis: 262142 Dauer 78,0044 ms
max Schwelle: 524288
maxEvenDividing Ergebnis: 524286 Dauer 109,0062 ms
maxEvenDividing2 Ergebnis: 524286 Dauer 78,0045 ms
maxEvenConjunction Ergebnis: 524286 Dauer 109,0062 ms
maxEvenConjunction2 Ergebnis: 524286 Dauer 80,0046 ms
max Schwelle: 1048576
maxEvenDividing Ergebnis: 1048574 Dauer 109,0063 ms
maxEvenDividing2 Ergebnis: 1048574 Dauer 80,0045 ms
maxEvenConjunction Ergebnis: 1048574 Dauer 114,0066 ms
maxEvenConjunction2 Ergebnis: 1048574 Dauer 78,0044 ms
max Schwelle: 2097152
maxEvenDividing Ergebnis: 2097150 Dauer 111,0064 ms
maxEvenDividing2 Ergebnis: 2097150 Dauer 79,0045 ms
maxEvenConjunction Ergebnis: 2097150 Dauer 112,0064 ms
maxEvenConjunction2 Ergebnis: 2097150 Dauer 77,0044 ms
max Schwelle: 4194304
maxEvenDividing Ergebnis: 4194302 Dauer 111,0063 ms
maxEvenDividing2 Ergebnis: 4194302 Dauer 78,0045 ms
maxEvenConjunction Ergebnis: 4194302 Dauer 111,0063 ms
maxEvenConjunction2 Ergebnis: 4194302 Dauer 77,0044 ms
max Threshold: 8388608
maxEvenDividing Ergebnis: 8388606 Dauer 109,0062 ms
maxEvenDividing2 Ergebnis: 8388606 Dauer 78,0045 ms
maxEvenConjunction Ergebnis: 8388606 Dauer 114,0065 ms
maxEvenConjunction2 Ergebnis: 8388606 Dauer 78,0045 ms
max Threshold: 16777216
maxEvenDividing Ergebnis: 16777214 Dauer 109,0062 ms
maxEvenDividing2 Ergebnis: 16777214 Dauer 77,0044 ms
maxEvenConjunction Ergebnis: 16777214 Dauer 109,0063 ms
maxEvenConjunction2 Ergebnis: 16777214 Dauer 77,0044 ms
max Threshold: 33554432
maxEvenDividing Ergebnis: 33554430 Dauer 113,0065 ms
maxEvenDividing2 Ergebnis: 33554430 Dauer 78,0045 ms
maxEvenConjunction Ergebnis: 33554430 Dauer 110.0063 ms
maxEvenConjunction2 Ergebnis: 33554430 Dauer 80,0045 ms
max Threshold: 67108864
maxEvenDividing Ergebnis: 67108860 Dauer 112,0064 ms
maxEvenDividing2 Ergebnis: 67108860 Dauer 77,0044 ms
maxEvenConjunction Ergebnis: 67108860 Dauer 112,0064 ms
maxEvenConjunction2 Ergebnis: 67108860 Dauer 80,0046 ms
max Threshold: 134217728
maxEvenDividing Ergebnis: 134217726 Dauer 109,0063 ms
maxEvenDividing2 Ergebnis: 134217726 Dauer 78,0044 ms
maxEvenConjunction Ergebnis: 134217726 Dauer 114,0065 ms
maxEvenConjunction2 Ergebnis: 134217726 Dauer 81,0047 ms
max Threshold: 268435456
maxEvenDividing Ergebnis: 268435446 Dauer 111,0064 ms
maxEvenDividing2 Ergebnis: 268435446 Dauer 79,0045 ms
maxEvenConjunction Ergebnis: 268435446 Dauer 114,0065 ms
maxEvenConjunction2 Ergebnis: 268435446 Dauer 79,0045 ms
maximale Schwelle: 536870912
maxEvenDividing Ergebnis: 536870910 Dauer 107,0062 ms
maxEvenDividing2 Ergebnis: 536870910 Dauer 76,0043 ms
maxEvenConjunction Ergebnis: 536870910 Dauer 109,0062 ms
maxEvenConjunction2 Ergebnis: 536870910 Dauer 80,0046 ms
Ich habe keine klare Erklärung gefunden, warum der Go- Compiler den Code nicht optimiert und immer die zweite Bedingung überprüft, auch wenn die erste falsch ist – vielleicht habe ich einfach nicht das Offensichtliche gesehen? Oder muss ich dem Compiler spezielle Anweisungen geben? Ich würde mich über hilfreiche Kommentare freuen.
PS: Ja, aus Interesse habe ich ähnliche Tests mit Java 5 und Java 7/8 durchgeführt – alles klar, die Ausführungszeit ist gleich.
Quelle: habr.com
