Denkt u dat deze twee manieren om condities binnen een lus te controleren qua prestaties gelijkwaardig zijn?
if a > b && c*2 > d {
....
}
// en
if a d {
....
}
Alles begon met een "warming-up voor de geest"; er moest een voorbeeld worden gegeven van een optimale zoektocht naar het grootste even getal in een array van gehele getallen [-x….x]. Ik was benieuwd hoe veel beter de prestaties zouden zijn als we logische vermenigvuldiging met 1 gebruiken om te bepalen of een getal even is of niet.
//у четных чисел последний бит всегда равен 0
value & 1 == 0
//vs классический метод
value % 2 == 0
Mijn ervaring met programmeren in Go is niet zo groot, iets meer dan anderhalf jaar; ik heb het vaak gebruikt, maar vooral voor utilitaire doeleinden (misschien met uitzondering van één project dat verband hield met een hoogbelast http-serviced). Daarom ben ik precies met dit begonnen. We openen GoLand en schrijven een simpele 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("begin capaciteit van de array: " + fmt.Sprint(size))
var maxValue int32
// We zullen het bereik van getallen variëren van minimaal
// tot maximaal. Hoe kleiner het bereik, hoe meer
// processor tijd er nodig is voor de operatie
// van het vergelijken van het huidige getal met eerder gevonden getallen.
for maxValue = 128; maxValue We krijgen een resultaat te zien waaruit blijkt dat hoe hoger de drempel, hoe vaker er fluctuaties in de prestaties optreden.
Vergelijkmaximale drempel: 128
maxEvenDividing resultaat: 126 duur 116.0067ms
maxEvenConjunction resultaat: 126 duur 116.0066ms
max drempel: 16384
maxEvenDividing resultaat: 16382 duur 115.0066ms
maxEvenConjunction resultaat: 16382 duur 111.0064ms
......
max drempel: 8388608
maxEvenDividing resultaat: 8388606 duur 109.0063ms
maxEvenConjunction resultaat: 8388606 duur 109.0062ms
maxdrempel: 16777216
maxEvenDividing resultaat: 16777214 duur 108.0062ms
maxEvenConjunction resultaat: 16777214 duur 109.0062ms
maxdrempel: 33554432
maxEvenDividing resultaat: 33554430 duur 114.0066ms
maxEvenConjunction resultaat: 33554430 duur 110.0063ms
maxdrempel: 67108864
maxEvenDividing resultaat: 67108860 duur 111.0064ms
maxEvenConjunction resultaat: 67108860 duur 109.0062ms
maxdrempel: 134217728
maxEvenDividing resultaat: 134217726 duur 108.0062ms
maxEvenConjunction resultaat: 134217726 duur 109.0063ms
maxdrempel: 268435456
maxEvenDividing resultaat: 268435446 duur 111.0063ms
maxEvenConjunction resultaat: 268435446 duur 110.0063ms
Het is duidelijk dat we in dit geval, voor verschillende drempels, verschillende datasets hebben, de CPU-belasting (op mijn laptop i5-2540M) varieert tussen de 20-30%, het geheugen dat door de applicatie in GoLand gemiddeld wordt gebruikt is ongeveer 813MB — dit beïnvloedt ook de nauwkeurigheid van de resultaten, we moeten het opslaan van testsets op de schijf implementeren en alle tests voor elke drempel afzonderlijk draaien.
En terwijl ik nadenk over hoe ik dit met minimale kosten kan implementeren, corrigeer ik automatisch de voorwaarde.
if value > current && value&1 == 0 {
current = value
}
en een werkende opdracht krijgen.
if value <= current {
continue;
}
if value&1 == 0 {
current = value
}
ik start de tests opnieuw… en begrijp niets meer 🙂
De tijd die nodig is om de uitvoering uit te voeren begint niet langer in percentage of fracties van procenten te verschillen, maar met 10-15%. Ik schrijf nog snel 2 tests erbij:
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
}
ik start en krijg dit beeld:initiële arraycapaciteit: 100000000
maximale drempel: 128
maxEvenDividing resultaat: 126 duur 116.0066ms
maxEvenDividing2 resultaat: 126 duur 79.0045ms
maxEvenConjunction resultaat: 126 duur 114.0065ms
maxEvenConjunction2 resultaat: 126 duur 83.0048ms
maxdrempel: 256
maxEvenDividing resultaat: 254 duur 111.0063ms
maxEvenDividing2 resultaat: 254 duur 77.0044ms
maxEvenConjunction resultaat: 254 duur 110.0063ms
maxEvenConjunction2 resultaat: 254 duur 80.0046ms
maxdrempel: 512
maxEvenDividing resultaat: 510 duur 114.0066ms
maxEvenDividing2 resultaat: 510 duur 80.0045ms
maxEvenConjunction resultaat: 510 duur 110.0063ms
maxEvenConjunction2 resultaat: 510 duur 80.0046ms
maxdrempel: 1024
maxEvenDividing resultaat: 1022 duur 109.0063ms
maxEvenDividing2 resultaat: 1022 duur 77.0044ms
maxEvenConjunction resultaat: 1022 duur 111.0063ms
maxEvenConjunction2 resultaat: 1022 duur 81.0047ms
maxdrempel: 2048
maxEvenDividing resultaat: 2046 duur 114.0065ms
maxEvenDividing2 resultaat: 2046 duur 79.0045ms
maxEvenConjunction resultaat: 2046 duur 113.0065ms
maxEvenConjunction2 resultaat: 2046 duur 81.0046ms
max drempel: 4096
maxEvenDividing resultaat: 4094 duur 114.0065ms
maxEvenDividing2 resultaat: 4094 duur 80.0046ms
maxEvenConjunction resultaat: 4094 duur 111.0063ms
maxEvenConjunction2 resultaat: 4094 duur 78.0045ms
max drempel: 8192
maxEvenDividing resultaat: 8190 duur 107.0062ms
maxEvenDividing2 resultaat: 8190 duur 77.0044ms
maxEvenConjunction resultaat: 8190 duur 111.0063ms
maxEvenConjunction2 resultaat: 8190 duur 77.0044ms
max drempel: 16384
maxEvenDividing resultaat: 16382 duur 109.0063ms
maxEvenDividing2 resultaat: 16382 duur 77.0044ms
maxEvenConjunction resultaat: 16382 duur 108.0062ms
maxEvenConjunction2 resultaat: 16382 duur 77.0044ms
max drempel: 32768
maxEvenDividing resultaat: 32766 duur 112.0064ms
maxEvenDividing2 resultaat: 32766 duur 77.0044ms
maxEvenConjunction resultaat: 32766 duur 109.0062ms
maxEvenConjunction2 resultaat: 32766 duur 78.0045ms
max drempel: 65536
maxEvenDividing resultaat: 65534 duur 109.0062ms
maxEvenDividing2 resultaat: 65534 duur 75.0043ms
maxEvenConjunction resultaat: 65534 duur 109.0063ms
maxEvenConjunction2 resultaat: 65534 duur 79.0045ms
max drempel: 131072
maxEvenDividing resultaat: 131070 duur 108.0061ms
maxEvenDividing2 resultaat: 131070 duur 76.0044ms
maxEvenConjunction resultaat: 131070 duur 110.0063ms
maxEvenConjunction2 resultaat: 131070 duur 80.0046ms
max drempel: 262144
maxEvenDividing resultaat: 262142 duur 110.0063ms
maxEvenDividing2 resultaat: 262142 duur 76.0044ms
maxEvenConjunction resultaat: 262142 duur 107.0061ms
maxEvenConjunction2 resultaat: 262142 duur 78.0044ms
max drempel: 524288
maxEvenDividing resultaat: 524286 duur 109.0062ms
maxEvenDividing2 resultaat: 524286 duur 78.0045ms
maxEvenConjunction resultaat: 524286 duur 109.0062ms
maxEvenConjunction2 resultaat: 524286 duur 80.0046ms
max drempel: 1048576
maxEvenDividing resultaat: 1048574 duur 109.0063ms
maxEvenDividing2 resultaat: 1048574 duur 80.0045ms
maxEvenConjunction resultaat: 1048574 duur 114.0066ms
maxEvenConjunction2 resultaat: 1048574 duur 78.0044ms
max drempel: 2097152
maxEvenDividing resultaat: 2097150 duur 111.0064ms
maxEvenDividing2 resultaat: 2097150 duur 79.0045ms
maxEvenConjunction resultaat: 2097150 duur 112.0064ms
maxEvenConjunction2 resultaat: 2097150 duur 77.0044ms
max drempel: 4194304
maxEvenDividing resultaat: 4194302 duur 111.0063ms
maxEvenDividing2 resultaat: 4194302 duur 78.0045ms
maxEvenConjunction resultaat: 4194302 duur 111.0063ms
maxEvenConjunction2 resultaat: 4194302 duur 77.0044ms
max drempel: 8388608
maxEvenDividing resultaat: 8388606 duur 109.0062ms
maxEvenDividing2 resultaat: 8388606 duur 78.0045ms
maxEvenConjunction resultaat: 8388606 duur 114.0065ms
maxEvenConjunction2 resultaat: 8388606 duur 78.0045ms
maxdrempel: 16777216
maxEvenDividing resultaat: 16777214 duur 109.0062ms
maxEvenDividing2 resultaat: 16777214 duur 77.0044ms
maxEvenConjunction resultaat: 16777214 duur 109.0063ms
maxEvenConjunction2 resultaat: 16777214 duur 77.0044ms
maxdrempel: 33554432
maxEvenDividing resultaat: 33554430 duur 113.0065ms
maxEvenDividing2 resultaat: 33554430 duur 78.0045ms
maxEvenConjunction resultaat: 33554430 duur 110.0063ms
maxEvenConjunction2 resultaat: 33554430 duur 80.0045ms
maxdrempel: 67108864
maxEvenDividing resultaat: 67108860 duur 112.0064ms
maxEvenDividing2 resultaat: 67108860 duur 77.0044ms
maxEvenConjunction resultaat: 67108860 duur 112.0064ms
maxEvenConjunction2 resultaat: 67108860 duur 80.0046ms
maxdrempel: 134217728
maxEvenDividing resultaat: 134217726 duur 109.0063ms
maxEvenDividing2 resultaat: 134217726 duur 78.0044ms
maxEvenConjunction resultaat: 134217726 duur 114.0065ms
maxEvenConjunction2 resultaat: 134217726 duur 81.0047ms
maxdrempel: 268435456
maxEvenDividing resultaat: 268435446 duur 111.0064ms
maxEvenDividing2 resultaat: 268435446 duur 79.0045ms
maxEvenConjunction resultaat: 268435446 duur 114.0065ms
maxEvenConjunction2 resultaat: 268435446 duur 79.0045ms
max drempel: 536870912
maxEvenDividing resultaat: 536870910 duur 107.0062ms
maxEvenDividing2 resultaat: 536870910 duur 76.0043ms
maxEvenConjunction resultaat: 536870910 duur 109.0062ms
maxEvenConjunction2 resultaat: 536870910 duur 80.0046ms
Een duidelijke uitleg waarom de Go-compiler de code niet optimaliseert en altijd de tweede voorwaarde controleert, zelfs als de eerste onwaar is, heb ik niet gevonden. Misschien ben ik gewoon blind voor een voor de hand liggende fout? Of moet ik de compiler speciale instructies geven? Ik zou blij zijn met nuttige opmerkingen.
PS: Ja, voor de grap heb ik vergelijkbare tests uitgevoerd op Java 5 en Java 7/8 — alles is duidelijk, de uitvoeringstijd is hetzelfde.
Bron: habr.com
