Cosa ne pensate, questi due modi di controllare le condizioni all'interno di un ciclo sono equivalenti in termini di prestazioni?
if a > b && c*2 > d {
....
}
// e
if a d {
....
}
Tutto è iniziato con un "riscaldamento per il cervello", dovevo fornire un esempio di ricerca ottimale in un array di numeri interi [-x….x] del numero pari più grande. Mi sono chiesto quanto sarebbe aumentata la prestazione se per verificare se un numero è pari o meno, utilizzassi la moltiplicazione logica per 1.
//у четных чисел последний бит всегда равен 0
value & 1 == 0
//vs классический метод
value % 2 == 0
La mia esperienza di programmazione in Go non è molto ampia, poco più di un anno e mezzo, l'ho utilizzato spesso, ma solo per scopi utilitari (forse a parte un progetto legato a un servizio HTTP ad alto carico), quindi ho iniziato proprio con quello. Apriamo GoLand e scriviamo un semplice 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("capacità iniziale dell'array: " + fmt.Sprint(size))
var maxValue int32
// Variaremo l'intervallo di numeri dal minimo
// al massimo. Più piccolo è l'intervallo, maggiore sarà
// il tempo della CPU per l'operazione di
// confronto del numero corrente con quello precedentemente trovato e viceversa
for maxValue = 128; maxValue Otteniamo un risultato che mostra come, all'aumentare della soglia, aumentino anche le fluttuazioni in termini di prestazioni.
Confrontasoglia massima: 128
risultato maxEvenDividing: 126 durata 116.0067ms
risultato maxEvenConjunction: 126 durata 116.0066ms
soglia massima: 16384
risultato di maxEvenDividing: 16382 durata 115.0066ms
risultato di maxEvenConjunction: 16382 durata 111.0064ms
......
soglia massima: 8388608
risultato di maxEvenDividing: 8388606 durata 109.0063ms
risultato maxEvenConjunction: 8388606 durata 109,0062ms
soglia massima: 16777216
risultato maxEvenDividing: 16777214 durata 108,0062ms
risultato maxEvenConjunction: 16777214 durata 109,0062ms
soglia massima: 33554432
risultato maxEvenDividing: 33554430 durata 114,0066ms
risultato maxEvenConjunction: 33554430 durata 110,0063ms
soglia massima: 67108864
risultato maxEvenDividing: 67108860 durata 111,0064ms
risultato maxEvenConjunction: 67108860 durata 109,0062ms
soglia massima: 134217728
risultato maxEvenDividing: 134217726 durata 108,0062ms
risultato maxEvenConjunction: 134217726 durata 109,0063ms
soglia massima: 268435456
risultato maxEvenDividing: 268435446 durata 111,0063ms
risultato maxEvenConjunction: 268435446 durata 110,0063ms
È chiaro che in questo caso, per diversi threshold abbiamo diversi set di dati di test, il carico della CPU (sul mio laptop i5-2540M) varia intorno al 20-30%, la memoria occupata dall'applicazione eseguita da GoLand è in media di circa 813MB — questo influisce anche sull'affidabilità del risultato, è necessario implementare il salvataggio dei set di test su disco e eseguire tutti i test per ogni soglia isolatamente.
E mentre rifletto su come realizzare tutto questo con il minimo costo, correggo meccanicamente il controllo della condizione
if value > current && value&1 == 0 {
current = value
}
in
if value <= current {
continue;
}
if value&1 == 0 {
current = value
}
eseguo di nuovo i test… e smetto di capire qualcosa 🙂
Il tempo impiegato per l'esecuzione inizia a variare non più di percentuali/frazioni percentuali, ma di 10-15%. Scrivo rapidamente altri 2 test:
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
}
eseguo e ottengo questa immagine:capacità iniziale dell'array: 100000000
soglia massima: 128
risultato maxEvenDividing: 126 durata 116,0066ms
risultato maxEvenDividing2: 126 durata 79,0045ms
risultato maxEvenConjunction: 126 durata 114,0065ms
risultato maxEvenConjunction2: 126 durata 83,0048ms
soglia massima: 256
risultato maxEvenDividing: 254 durata 111,0063ms
risultato maxEvenDividing2: 254 durata 77,0044ms
risultato maxEvenConjunction: 254 durata 110,0063ms
risultato maxEvenConjunction2: 254 durata 80,0046ms
soglia massima: 512
risultato maxEvenDividing: 510 durata 114,0066ms
risultato maxEvenDividing2: 510 durata 80,0045ms
risultato maxEvenConjunction: 510 durata 110,0063ms
risultato maxEvenConjunction2: 510 durata 80,0046ms
soglia massima: 1024
risultato maxEvenDividing: 1022 durata 109,0063ms
risultato maxEvenDividing2: 1022 durata 77,0044ms
risultato maxEvenConjunction: 1022 durata 111,0063ms
risultato maxEvenConjunction2: 1022 durata 81,0047ms
soglia massima: 2048
risultato maxEvenDividing: 2046 durata 114,0065ms
risultato maxEvenDividing2: 2046 durata 79,0045ms
risultato maxEvenConjunction: 2046 durata 113,0065ms
risultato maxEvenConjunction2: 2046 durata 81.0046ms
soglia massima: 4096
risultato maxEvenDividing: 4094 durata 114.0065ms
risultato maxEvenDividing2: 4094 durata 80.0046ms
risultato maxEvenConjunction: 4094 durata 111.0063ms
risultato maxEvenConjunction2: 4094 durata 78.0045ms
soglia massima: 8192
risultato maxEvenDividing: 8190 durata 107.0062ms
risultato maxEvenDividing2: 8190 durata 77.0044ms
risultato maxEvenConjunction: 8190 durata 111.0063ms
risultato maxEvenConjunction2: 8190 durata 77.0044ms
soglia massima: 16384
risultato maxEvenDividing: 16382 durata 109.0063ms
risultato maxEvenDividing2: 16382 durata 77.0044ms
risultato maxEvenConjunction: 16382 durata 108.0062ms
risultato maxEvenConjunction2: 16382 durata 77.0044ms
soglia massima: 32768
risultato maxEvenDividing: 32766 durata 112.0064ms
risultato maxEvenDividing2: 32766 durata 77.0044ms
risultato maxEvenConjunction: 32766 durata 109.0062ms
risultato maxEvenConjunction2: 32766 durata 78.0045ms
soglia massima: 65536
risultato maxEvenDividing: 65534 durata 109.0062ms
risultato maxEvenDividing2: 65534 durata 75.0043ms
risultato maxEvenConjunction: 65534 durata 109.0063ms
risultato maxEvenConjunction2: 65534 durata 79.0045ms
soglia massima: 131072
risultato maxEvenDividing: 131070 durata 108.0061ms
risultato maxEvenDividing2: 131070 durata 76.0044ms
risultato maxEvenConjunction: 131070 durata 110.0063ms
risultato maxEvenConjunction2: 131070 durata 80.0046ms
soglia massima: 262144
risultato maxEvenDividing: 262142 durata 110.0063ms
risultato maxEvenDividing2: 262142 durata 76.0044ms
risultato maxEvenConjunction: 262142 durata 107.0061ms
risultato maxEvenConjunction2: 262142 durata 78.0044ms
soglia massima: 524288
risultato maxEvenDividing: 524286 durata 109.0062ms
risultato maxEvenDividing2: 524286 durata 78.0045ms
risultato maxEvenConjunction: 524286 durata 109.0062ms
risultato maxEvenConjunction2: 524286 durata 80.0046ms
soglia massima: 1048576
risultato maxEvenDividing: 1048574 durata 109.0063ms
risultato maxEvenDividing2: 1048574 durata 80.0045ms
risultato maxEvenConjunction: 1048574 durata 114.0066ms
risultato maxEvenConjunction2: 1048574 durata 78.0044ms
soglia massima: 2097152
risultato maxEvenDividing: 2097150 durata 111.0064ms
risultato maxEvenDividing2: 2097150 durata 79.0045ms
risultato maxEvenConjunction: 2097150 durata 112.0064ms
risultato maxEvenConjunction2: 2097150 durata 77.0044ms
soglia massima: 4194304
risultato maxEvenDividing: 4194302 durata 111.0063ms
risultato maxEvenDividing2: 4194302 durata 78.0045ms
risultato maxEvenConjunction: 4194302 durata 111.0063ms
risultato maxEvenConjunction2: 4194302 durata 77.0044ms
soglia massima: 8388608
risultato maxEvenDividing: 8388606 durata 109.0062ms
risultato maxEvenDividing2: 8388606 durata 78.0045ms
risultato maxEvenConjunction: 8388606 durata 114.0065ms
risultato maxEvenConjunction2: 8388606 durata 78.0045ms
soglia massima: 16777216
risultato maxEvenDividing: 16777214 durata 109.0062ms
risultato maxEvenDividing2: 16777214 durata 77.0044ms
risultato maxEvenConjunction: 16777214 durata 109.0063ms
risultato maxEvenConjunction2: 16777214 durata 77.0044ms
soglia massima: 33554432
risultato maxEvenDividing: 33554430 durata 113.0065ms
risultato maxEvenDividing2: 33554430 durata 78.0045ms
risultato maxEvenConjunction: 33554430 durata 110,0063ms
risultato maxEvenConjunction2: 33554430 durata 80.0045ms
soglia massima: 67108864
risultato maxEvenDividing: 67108860 durata 112.0064ms
risultato maxEvenDividing2: 67108860 durata 77.0044ms
risultato maxEvenConjunction: 67108860 durata 112.0064ms
risultato maxEvenConjunction2: 67108860 durata 80.0046ms
soglia massima: 134217728
risultato maxEvenDividing: 134217726 durata 109.0063ms
risultato maxEvenDividing2: 134217726 durata 78.0044ms
risultato maxEvenConjunction: 134217726 durata 114.0065ms
risultato maxEvenConjunction2: 134217726 durata 81.0047ms
soglia massima: 268435456
risultato maxEvenDividing: 268435446 durata 111.0064ms
risultato maxEvenDividing2: 268435446 durata 79.0045ms
risultato maxEvenConjunction: 268435446 durata 114.0065ms
risultato maxEvenConjunction2: 268435446 durata 79.0045ms
soglia massima: 536870912
risultato maxEvenDividing: 536870910 durata 107.0062ms
risultato maxEvenDividing2: 536870910 durata 76.0043ms
risultato maxEvenConjunction: 536870910 durata 109.0062ms
risultato maxEvenConjunction2: 536870910 durata 80.0046ms
Non ho trovato una spiegazione chiara sul perché il compilatore Go non ottimizzi il codice e controlli sempre la seconda condizione, anche se la prima è falsa. Forse i miei occhi sono semplicemente "bruciati" e non vedo qualche errore ovvio? O è necessario fornire istruzioni particolari al compilatore? Sarebbe bello ricevere commenti costruttivi.
PS: Sì, per curiosità, ho eseguito test simili su Java 5 e Java 7/8: tutto chiaro, i tempi di esecuzione sono identici.
Fonte: habr.com
