Les conditions en Go et leurs étrangetés

Pensez-vous que ces deux variantes de vérification des conditions dans la boucle sont équivalentes en termes de performance ?

		
if a > b && c*2 > d {
	....
}
// et
if a  d {
 ....
}


Tout a commencé par un « échauffement pour le cerveau », il fallait donner un exemple de recherche optimale dans un tableau d'entiers [-x….x] du nombre pair le plus élevé. Je me suis demandé dans quelle mesure la performance serait améliorée si, pour déterminer si un nombre est pair ou non, on utilisait une multiplication logique par 1.


//у четных чисел последний бит всегда равен 0
value & 1 == 0
//vs классический метод
value % 2 == 0

Mon expérience en programmation Go n'est pas très vaste, un peu plus d'un an et demi, je l'ai utilisé assez souvent, mais principalement à des fins utilitaires (sauf peut-être pour un projet lié à un service HTTP hautement chargé), c'est donc avec lui que j'ai commencé. Ouvrons GoLand et écrivons un petit 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é initiale du tableau : " + fmt.Sprint(size))
	var maxValue int32
        // Nous allons varier la plage des nombres de la minimale 
        // à la maximale. Plus la plage est petite, plus 
        // le temps processeur sera consommé par l'opération 
        // de comparaison du nombre actuel avec celui précédemment trouvé et vice versa
	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
}

Nous obtenons un résultat, sur lequel il est visible que plus le seuil est élevé, plus les fluctuations en matière de performance sont fréquentes.

Comparerseuil max : 128
résultat maxEvenDividing : 126 durée 116.0067ms
résultat maxEvenConjunction : 126 durée 116.0066ms

seuil maximal : 16384
résultat maxEvenDividing : 16382 durée 115.0066ms
résultat maxEvenConjunction : 16382 durée 111.0064ms

......

seuil maximal : 8388608
résultat maxEvenDividing : 8388606 durée 109.0063ms
Résultat de maxEvenConjunction : 8388606 durée 109.0062ms

seuil maximum : 16777216
Résultat de maxEvenDividing : 16777214 durée 108.0062ms
Résultat de maxEvenConjunction : 16777214 durée 109.0062ms

seuil maximum : 33554432
Résultat de maxEvenDividing : 33554430 durée 114.0066ms
Résultat de maxEvenConjunction : 33554430 durée 110.0063ms

seuil maximum : 67108864
Résultat de maxEvenDividing : 67108860 durée 111.0064ms
Résultat de maxEvenConjunction : 67108860 durée 109.0062ms

seuil maximum : 134217728
Résultat de maxEvenDividing : 134217726 durée 108.0062ms
Résultat de maxEvenConjunction : 134217726 durée 109.0063ms

seuil maximum : 268435456
Résultat de maxEvenDividing : 268435446 durée 111.0063ms
Résultat de maxEvenConjunction : 268435446 durée 110.0063ms

Il est évident que, dans ce cas, pour différents seuils, nous avons différents ensembles de données de test. La charge processeur (sur mon portable i5-2540M) varie autour de 20 à 30 %, la mémoire occupée par l'application lancée depuis GoLand est en moyenne d'environ 813 Mo — cela influence également la fiabilité du résultat. Il est nécessaire de mettre en œuvre la sauvegarde des ensembles de test sur le disque et d'exécuter tous les tests pour chaque seuil de manière isolée.

En réfléchissant à la manière d'implémenter tout cela avec un minimum de coûts, je corrige machinalement la vérification de la condition.

		
if value > current && value&1 == 0 {
	current = value
}

sur

		
if value <= current {
        continue;
}
if value&1 == 0 {
	current = value
}

Je relance les tests encore une fois… et je ne comprends plus rien 🙂

Le temps d'exécution commence à varier non plus par pourcentages/décimes de pourcent, mais de 10 à 15 %. Je complète rapidement avec 2 autres tests :

		
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
}

Je lance et j'obtiens cette image :capacité initiale du tableau : 100000000

seuil max : 128
Résultat de maxEvenDividing : 126 durée 116.0066ms
Résultat de maxEvenDividing2 : 126 durée 79.0045ms
Résultat de maxEvenConjunction : 126 durée 114.0065ms
Résultat de maxEvenConjunction2 : 126 durée 83.0048ms

seuil maximum : 256
Résultat de maxEvenDividing : 254 durée 111.0063ms
Résultat de maxEvenDividing2 : 254 durée 77.0044ms
Résultat de maxEvenConjunction : 254 durée 110.0063ms
Résultat de maxEvenConjunction2 : 254 durée 80.0046ms

seuil maximum : 512
Résultat de maxEvenDividing : 510 durée 114.0066ms
Résultat de maxEvenDividing2 : 510 durée 80.0045ms
Résultat de maxEvenConjunction : 510 durée 110.0063ms
Résultat de maxEvenConjunction2 : 510 durée 80.0046ms

seuil maximum : 1024
Résultat de maxEvenDividing : 1022 durée 109.0063ms
Résultat de maxEvenDividing2 : 1022 durée 77.0044ms
Résultat de maxEvenConjunction : 1022 durée 111.0063ms
Résultat de maxEvenConjunction2 : 1022 durée 81.0047ms

seuil maximum : 2048
Résultat de maxEvenDividing : 2046 durée 114.0065ms
Résultat de maxEvenDividing2 : 2046 durée 79.0045ms
Résultat de maxEvenConjunction : 2046 durée 113.0065ms
maxEvenConjunction2 résultat : 2046 durée 81.0046ms

seuil maximum : 4096
maxEvenDividing résultat : 4094 durée 114.0065ms
maxEvenDividing2 résultat : 4094 durée 80.0046ms
maxEvenConjunction résultat : 4094 durée 111.0063ms
maxEvenConjunction2 résultat : 4094 durée 78.0045ms

seuil maximum : 8192
maxEvenDividing résultat : 8190 durée 107.0062ms
maxEvenDividing2 résultat : 8190 durée 77.0044ms
maxEvenConjunction résultat : 8190 durée 111.0063ms
maxEvenConjunction2 résultat : 8190 durée 77.0044ms

seuil maximal : 16384
maxEvenDividing résultat : 16382 durée 109.0063ms
maxEvenDividing2 résultat : 16382 durée 77.0044ms
maxEvenConjunction résultat : 16382 durée 108.0062ms
maxEvenConjunction2 résultat : 16382 durée 77.0044ms

seuil maximum : 32768
maxEvenDividing résultat : 32766 durée 112.0064ms
maxEvenDividing2 résultat : 32766 durée 77.0044ms
maxEvenConjunction résultat : 32766 durée 109.0062ms
maxEvenConjunction2 résultat : 32766 durée 78.0045ms

seuil maximum : 65536
maxEvenDividing résultat : 65534 durée 109.0062ms
maxEvenDividing2 résultat : 65534 durée 75.0043ms
maxEvenConjunction résultat : 65534 durée 109.0063ms
maxEvenConjunction2 résultat : 65534 durée 79.0045ms

seuil maximum : 131072
maxEvenDividing résultat : 131070 durée 108.0061ms
maxEvenDividing2 résultat : 131070 durée 76.0044ms
maxEvenConjunction résultat : 131070 durée 110.0063ms
maxEvenConjunction2 résultat : 131070 durée 80.0046ms

seuil maximum : 262144
maxEvenDividing résultat : 262142 durée 110.0063ms
maxEvenDividing2 résultat : 262142 durée 76.0044ms
maxEvenConjunction résultat : 262142 durée 107.0061ms
maxEvenConjunction2 résultat : 262142 durée 78.0044ms

seuil maximum : 524288
maxEvenDividing résultat : 524286 durée 109.0062ms
maxEvenDividing2 résultat : 524286 durée 78.0045ms
maxEvenConjunction résultat : 524286 durée 109.0062ms
maxEvenConjunction2 résultat : 524286 durée 80.0046ms

seuil maximum : 1048576
maxEvenDividing résultat : 1048574 durée 109.0063ms
maxEvenDividing2 résultat : 1048574 durée 80.0045ms
maxEvenConjunction résultat : 1048574 durée 114.0066ms
maxEvenConjunction2 résultat : 1048574 durée 78.0044ms

seuil maximum : 2097152
maxEvenDividing résultat : 2097150 durée 111.0064ms
maxEvenDividing2 résultat : 2097150 durée 79.0045ms
maxEvenConjunction résultat : 2097150 durée 112.0064ms
maxEvenConjunction2 résultat : 2097150 durée 77.0044ms

seuil maximum : 4194304
maxEvenDividing résultat : 4194302 durée 111.0063ms
maxEvenDividing2 résultat : 4194302 durée 78.0045ms
maxEvenConjunction résultat : 4194302 durée 111.0063ms
maxEvenConjunction2 résultat : 4194302 durée 77.0044ms

seuil maximal : 8388608
maxEvenDividing résultat : 8388606 durée 109.0062ms
maxEvenDividing2 résultat : 8388606 durée 78.0045ms
maxEvenConjunction résultat : 8388606 durée 114.0065ms
maxEvenConjunction2 résultat : 8388606 durée 78.0045ms

seuil maximum : 16777216
maxEvenDividing résultat : 16777214 durée 109.0062ms
maxEvenDividing2 résultat : 16777214 durée 77.0044ms
maxEvenConjunction résultat : 16777214 durée 109.0063ms
maxEvenConjunction2 résultat : 16777214 durée 77.0044ms

seuil maximum : 33554432
maxEvenDividing résultat : 33554430 durée 113.0065ms
maxEvenDividing2 résultat : 33554430 durée 78.0045ms
Résultat de maxEvenConjunction : 33554430 durée 110.0063ms
maxEvenConjunction2 résultat : 33554430 durée 80.0045ms

seuil maximum : 67108864
maxEvenDividing résultat : 67108860 durée 112.0064ms
maxEvenDividing2 résultat : 67108860 durée 77.0044ms
maxEvenConjunction résultat : 67108860 durée 112.0064ms
maxEvenConjunction2 résultat : 67108860 durée 80.0046ms

seuil maximum : 134217728
maxEvenDividing résultat : 134217726 durée 109.0063ms
maxEvenDividing2 résultat : 134217726 durée 78.0044ms
maxEvenConjunction résultat : 134217726 durée 114.0065ms
maxEvenConjunction2 résultat : 134217726 durée 81.0047ms

seuil maximum : 268435456
maxEvenDividing résultat : 268435446 durée 111.0064ms
maxEvenDividing2 résultat : 268435446 durée 79.0045ms
maxEvenConjunction résultat : 268435446 durée 114.0065ms
maxEvenConjunction2 résultat : 268435446 durée 79.0045ms

seuil maximum : 536870912
maxEvenDividing résultat : 536870910 durée 107.0062ms
maxEvenDividing2 résultat : 536870910 durée 76.0043ms
résultat de maxEvenConjunction : 536870910 durée 109.0062ms
résultat de maxEvenConjunction2 : 536870910 durée 80.0046ms

Je n'ai pas trouvé d'explication claire sur pourquoi le compilateur Go n'optimise pas le code et vérifie toujours la seconde condition, même si la première est fausse. Peut-être que mes yeux sont « fatigués » et que je ne vois pas une erreur évidente ? Ou faut-il indiquer des instructions particulières au compilateur ? Je serais ravi de commentaires éclairants.

PS : Oui, par curiosité, j'ai effectué des tests similaires sur Java 5 et Java 7/8 — tout est clair, le temps d'exécution est identique.

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster