Le condizioni in Go e le loro stranezze

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  current && value%2 == 0 {
			current = value
		}
	}
	duration := time.Since(start)
	result := Result{name, duration, current}
	return result
}

func maxEvenConjunction(name string, arr []int32) Result {
	start := time.Now()
	var current int32 = math.MinInt32
	for _, value := range arr {
		if value > current && value&1 == 0 {
			current = value
		}
	}
	duration := time.Since(start)
	result := Result{name, duration, current}
	return result
}

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

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster