Cosa ne pensi, questi due metodi di controllo delle 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 la mente', dove dovevo dare un esempio di ricerca ottimale in un array di numeri interi [-x….x] per trovare il maggiore numero pari. Mi sono chiesto quanto sarebbe migliorata la performance se per determinare se un numero è pari o meno, si utilizzasse la moltiplicazione logica per 1.
//у четных чисел последний бит всегда равен 0
value & 1 == 0
//vs классический метод
value % 2 == 0
La mia esperienza nella programmazione in Go non è molto ampia, poco più di un anno e mezzo; anche se lo ho usato frequentemente, lo ho fatto principalmente per scopi utilitari (tranne forse in un progetto legato a un servizio http ad alta intensità di carico), quindi ho cominciato proprio con questo. 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
// Variamo l'intervallo dei numeri dal minimo
// al massimo. Maggiore è l'intervallo, più tempo
// CPU sarà necessario per l'operazione
// di confronto tra il numero attuale e quello precedentemente trovato e viceversa
for maxValue = 128; maxValue I risultati mostrano che all'aumentare della soglia, aumentano anche le fluttuazioni delle prestazioni.
Confrontasoglia massima: 128
risultato maxEvenDividing: 126 durata 116.0067ms
risultato maxEvenConjunction: 126 durata 116.0066ms
soglia max: 16384
risultato maxEvenDividing: 16382 durata 115.0066ms
risultato maxEvenConjunction: 16382 durata 111.0064ms
......
soglia max: 8388608
risultato maxEvenDividing: 8388606 durata 109.0063ms
risultato maxEvenConjunction: 8388606 durata 109.0062ms
soglia max: 16777216
risultato maxEvenDividing: 16777214 durata 108.0062ms
risultato maxEvenConjunction: 16777214 durata 109.0062ms
soglia max: 33554432
risultato maxEvenDividing: 33554430 durata 114.0066ms
risultato maxEvenConjunction: 33554430 durata 110.0063ms
soglia max: 67108864
risultato maxEvenDividing: 67108860 durata 111.0064ms
risultato maxEvenConjunction: 67108860 durata 109.0062ms
soglia max: 134217728
risultato maxEvenDividing: 134217726 durata 108.0062ms
risultato maxEvenConjunction: 134217726 durata 109.0063ms
soglia max: 268435456
risultato maxEvenDividing: 268435446 durata 111.0063ms
risultato maxEvenConjunction: 268435446 durata 110.0063ms
È chiaro che in questo caso, per soglie diverse abbiamo diversi set di dati di test; il carico della CPU (sul mio laptop i5-2540M) varia attorno al 20..30%, la memoria occupata dall'applicazione eseguita da GoLand è in media di circa 813MB — questo influisce anche sulla validità dei risultati, è necessario implementare il salvataggio dei set di test su disco e eseguire tutti i test per ogni soglia in modo isolato.
E mentre rifletto su come realizzare tutto questo con il minimo sforzo, correggo automaticamente il controllo della condizione
if value > current && value&1 == 0 {
current = value
}
con
if value <= current {
continue;
}
if value&1 == 0 {
current = value
}
rilancio i test ancora una volta… e smetto di capire qualcosa 🙂
Il tempo impiegato per l'esecuzione inizia a differire non più di percentuali/frammenti di percentuale, ma del 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
}
rilancio 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
maxEvenDividing2 risultato: 510 durata 80.0045ms
maxEvenConjunction risultato: 510 durata 110.0063ms
maxEvenConjunction2 risultato: 510 durata 80.0046ms
soglia massima: 1024
maxEvenDividing risultato: 1022 durata 109.0063ms
maxEvenDividing2 risultato: 1022 durata 77.0044ms
maxEvenConjunction risultato: 1022 durata 111.0063ms
maxEvenConjunction2 risultato: 1022 durata 81.0047ms
soglia massima: 2048
maxEvenDividing risultato: 2046 durata 114.0065ms
maxEvenDividing2 risultato: 2046 durata 79.0045ms
maxEvenConjunction risultato: 2046 durata 113.0065ms
maxEvenConjunction2 risultato: 2046 durata 81.0046ms
soglia massima: 4096
maxEvenDividing risultato: 4094 durata 114.0065ms
maxEvenDividing2 risultato: 4094 durata 80.0046ms
maxEvenConjunction risultato: 4094 durata 111.0063ms
maxEvenConjunction2 risultato: 4094 durata 78.0045ms
soglia massima: 8192
maxEvenDividing risultato: 8190 durata 107.0062ms
maxEvenDividing2 risultato: 8190 durata 77.0044ms
maxEvenConjunction risultato: 8190 durata 111.0063ms
maxEvenConjunction2 risultato: 8190 durata 77.0044ms
soglia max: 16384
maxEvenDividing risultato: 16382 durata 109.0063ms
maxEvenDividing2 risultato: 16382 durata 77.0044ms
maxEvenConjunction risultati: 16382 durata 108.0062ms
maxEvenConjunction2 risultato: 16382 durata 77.0044ms
soglia massima: 32768
maxEvenDividing risultato: 32766 durata 112.0064ms
maxEvenDividing2 risultato: 32766 durata 77.0044ms
maxEvenConjunction risultato: 32766 durata 109.0062ms
maxEvenConjunction2 risultato: 32766 durata 78.0045ms
soglia massima: 65536
maxEvenDividing risultato: 65534 durata 109.0062ms
maxEvenDividing2 risultato: 65534 durata 75.0043ms
maxEvenConjunction risultato: 65534 durata 109.0063ms
maxEvenConjunction2 risultato: 65534 durata 79.0045ms
soglia massima: 131072
maxEvenDividing risultato: 131070 durata 108.0061ms
maxEvenDividing2 risultato: 131070 durata 76.0044ms
maxEvenConjunction risultato: 131070 durata 110.0063ms
maxEvenConjunction2 risultato: 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 max: 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 max: 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 max: 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 max: 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 max: 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 max: 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
non ho trovato una spiegazione chiara sul perché il compilatore Go non ottimizza il codice e controlla sempre la seconda condizione, anche se la prima è falsa — o forse il mio occhio si è «assuefatto» e non vedo un errore evidente? O è necessario fornire istruzioni particolari al compilatore? Sarebbe utile avere commenti chiari.
maxEvenConjunction2 risultato: 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. Oppure forse i miei occhi si sono 'abituati' e non vedo qualche errore ovvio? O è necessario fornire istruzioni particolari al compilatore? Sarei felice di ricevere commenti utili.
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
