Condiciones en Go y sus peculiaridades

¿Crees que estos dos métodos de comprobación de condiciones dentro de un bucle son equivalentes en términos de rendimiento?

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


Todo comenzó con un "calentamiento mental", donde tenía que presentar un ejemplo de búsqueda óptima en un arreglo de números enteros [-x….x] para encontrar el número par más grande. Me intrigó cuánto mejor sería el rendimiento si se usara la multiplicación lógica por 1 para determinar si un número es par o no.


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

Mi experiencia programando en Go no es muy extensa, poco más de un año y medio; aunque lo he utilizado con frecuencia, ha sido principalmente para fines utilitarios (quizás exceptuando un proyecto relacionado con un servicio http de alta carga), así que comencé con eso. Abrimos GoLand y escribimos una simple prueba.


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("capacidad inicial del arreglo: " + fmt.Sprint(size))
	var maxValue int32
        // Vararemos el rango de números desde el mínimo
        // hasta el máximo. Cuanto menor sea el rango, más
        // tiempo de CPU se gastará en la operación
        // de comparación del número actual con el hallado previamente
	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
}

Obtenemos un resultado que muestra que a medida que aumenta el umbral, también aparecen con mayor frecuencia fluctuaciones en el rendimiento.

Compararumbral máximo: 128
resultado de maxEvenDividing: 126 duración 116.0067ms
resultado de maxEvenConjunction: 126 duración 116.0066ms

umbral máximo: 16384
resultado de maxEvenDividing: 16382 duración 115.0066ms
resultado de maxEvenConjunction: 16382 duración 111.0064ms

......

umbral máximo: 8388608
resultado de maxEvenDividing: 8388606 duración 109.0063ms
resultado de maxEvenConjunction: 8388606 duración 109.0062ms

umbral máximo: 16777216
resultado de maxEvenDividing: 16777214 duración 108.0062ms
resultado de maxEvenConjunction: 16777214 duración 109.0062ms

umbral máximo: 33554432
resultado de maxEvenDividing: 33554430 duración 114.0066ms
resultado de maxEvenConjunction: 33554430 duración 110.0063ms

umbral máximo: 67108864
resultado de maxEvenDividing: 67108860 duración 111.0064ms
resultado de maxEvenConjunction: 67108860 duración 109.0062ms

umbral máximo: 134217728
resultado de maxEvenDividing: 134217726 duración 108.0062ms
resultado de maxEvenConjunction: 134217726 duración 109.0063ms

umbral máximo: 268435456
resultado de maxEvenDividing: 268435446 duración 111.0063ms
resultado de maxEvenConjunction: 268435446 duración 110.0063ms

Es evidente que en este caso, para diferentes umbrales, tenemos diferentes conjuntos de datos de prueba, la carga de la CPU (en mi portátil i5-2540M) varía alrededor del 20 al 30%, la memoria ocupada por la aplicación ejecutada desde GoLand es de aproximadamente 813MB, lo que también afecta la validez del resultado, es necesario implementar el almacenamiento de conjuntos de pruebas en el disco y ejecutar todas las pruebas para cada umbral de forma aislada.

Y mientras reflexionaba sobre cómo implementar todo esto con el menor costo posible, corrijo automáticamente la verificación de la condición

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

en

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

ejecuto las pruebas una vez más... y dejo de entender algo 🙂

El tiempo dedicado a la ejecución comienza a diferir no en porcentajes o fracciones de porcentaje, sino en un 10 a 15%. Rápidamente escribo 2 pruebas más:

		
func maxEvenDividing2(nombre 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{nombre, duration, current}
	return result
}

func maxEvenConjunction2(nombre 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{nombre, duration, current}
	return result
}

ejecuto y obtengo esta imagen:capacidad inicial del arreglo: 100000000

umbral máximo: 128
resultado de maxEvenDividing: 126 duración 116.0066ms
resultado de maxEvenDividing2: 126 duración 79.0045ms
resultado de maxEvenConjunction: 126 duración 114.0065ms
resultado de maxEvenConjunction2: 126 duración 83.0048ms

umbral máximo: 256
resultado de maxEvenDividing: 254 duración 111.0063ms
resultado de maxEvenDividing2: 254 duración 77.0044ms
resultado de maxEvenConjunction: 254 duración 110.0063ms
resultado de maxEvenConjunction2: 254 duración 80.0046ms

umbral máximo: 512
resultado de maxEvenDividing: 510 duración 114.0066ms
resultado de maxEvenDividing2: 510 duración 80.0045ms
resultado de maxEvenConjunction: 510 duración 110.0063ms
resultado de maxEvenConjunction2: 510 duración 80.0046ms

umbral máximo: 1024
resultado de maxEvenDividing: 1022 duración 109.0063ms
resultado de maxEvenDividing2: 1022 duración 77.0044ms
resultado de maxEvenConjunction: 1022 duración 111.0063ms
resultado de maxEvenConjunction2: 1022 duración 81.0047ms

umbral máximo: 2048
resultado de maxEvenDividing: 2046 duración 114.0065ms
resultado de maxEvenDividing2: 2046 duración 79.0045ms
resultado de maxEvenConjunction: 2046 duración 113.0065ms
maxEvenConjunction2 resultado: 2046 duración 81.0046ms

umbral máximo: 4096
maxEvenDividing resultado: 4094 duración 114.0065ms
maxEvenDividing2 resultado: 4094 duración 80.0046ms
maxEvenConjunction resultado: 4094 duración 111.0063ms
maxEvenConjunction2 resultado: 4094 duración 78.0045ms

umbral máximo: 8192
maxEvenDividing resultado: 8190 duración 107.0062ms
maxEvenDividing2 resultado: 8190 duración 77.0044ms
maxEvenConjunction resultado: 8190 duración 111.0063ms
maxEvenConjunction2 resultado: 8190 duración 77.0044ms

umbral máximo: 16384
maxEvenDividing resultado: 16382 duración 109.0063ms
maxEvenDividing2 resultado: 16382 duración 77.0044ms
maxEvenConjunction resultado: 16382 duración 108.0062ms
maxEvenConjunction2 resultado: 16382 duración 77.0044ms

umbral máximo: 32768
maxEvenDividing resultado: 32766 duración 112.0064ms
maxEvenDividing2 resultado: 32766 duración 77.0044ms
maxEvenConjunction resultado: 32766 duración 109.0062ms
maxEvenConjunction2 resultado: 32766 duración 78.0045ms

umbral máximo: 65536
maxEvenDividing resultado: 65534 duración 109.0062ms
maxEvenDividing2 resultado: 65534 duración 75.0043ms
maxEvenConjunction resultado: 65534 duración 109.0063ms
maxEvenConjunction2 resultado: 65534 duración 79.0045ms

umbral máximo: 131072
maxEvenDividing resultado: 131070 duración 108.0061ms
maxEvenDividing2 resultado: 131070 duración 76.0044ms
maxEvenConjunction resultado: 131070 duración 110.0063ms
maxEvenConjunction2 resultado: 131070 duración 80.0046ms

umbral máximo: 262144
maxEvenDividing resultado: 262142 duración 110.0063ms
maxEvenDividing2 resultado: 262142 duración 76.0044ms
maxEvenConjunction resultado: 262142 duración 107.0061ms
maxEvenConjunction2 resultado: 262142 duración 78.0044ms

umbral máximo: 524288
maxEvenDividing resultado: 524286 duración 109.0062ms
maxEvenDividing2 resultado: 524286 duración 78.0045ms
maxEvenConjunction resultado: 524286 duración 109.0062ms
maxEvenConjunction2 resultado: 524286 duración 80.0046ms

umbral máximo: 1048576
maxEvenDividing resultado: 1048574 duración 109.0063ms
maxEvenDividing2 resultado: 1048574 duración 80.0045ms
maxEvenConjunction resultado: 1048574 duración 114.0066ms
maxEvenConjunction2 resultado: 1048574 duración 78.0044ms

umbral máximo: 2097152
maxEvenDividing resultado: 2097150 duración 111.0064ms
maxEvenDividing2 resultado: 2097150 duración 79.0045ms
maxEvenConjunction resultado: 2097150 duración 112.0064ms
maxEvenConjunction2 resultado: 2097150 duración 77.0044ms

umbral máximo: 4194304
maxEvenDividing resultado: 4194302 duración 111.0063ms
maxEvenDividing2 resultado: 4194302 duración 78.0045ms
maxEvenConjunction resultado: 4194302 duración 111.0063ms
maxEvenConjunction2 resultado: 4194302 duración 77.0044ms

umbral máximo: 8388608
maxEvenDividing resultado: 8388606 duración 109.0062ms
maxEvenDividing2 resultado: 8388606 duración 78.0045ms
maxEvenConjunction resultado: 8388606 duración 114.0065ms
maxEvenConjunction2 resultado: 8388606 duración 78.0045ms

umbral máximo: 16777216
maxEvenDividing resultado: 16777214 duración 109.0062ms
maxEvenDividing2 resultado: 16777214 duración 77.0044ms
maxEvenConjunction resultado: 16777214 duración 109.0063ms
maxEvenConjunction2 resultado: 16777214 duración 77.0044ms

umbral máximo: 33554432
maxEvenDividing resultado: 33554430 duración 113.0065ms
maxEvenDividing2 resultado: 33554430 duración 78.0045ms
resultado de maxEvenConjunction: 33554430 duración 110.0063ms
maxEvenConjunction2 resultado: 33554430 duración 80.0045ms

umbral máximo: 67108864
maxEvenDividing resultado: 67108860 duración 112.0064ms
maxEvenDividing2 resultado: 67108860 duración 77.0044ms
maxEvenConjunction resultado: 67108860 duración 112.0064ms
maxEvenConjunction2 resultado: 67108860 duración 80.0046ms

umbral máximo: 134217728
maxEvenDividing resultado: 134217726 duración 109.0063ms
maxEvenDividing2 resultado: 134217726 duración 78.0044ms
maxEvenConjunction resultado: 134217726 duración 114.0065ms
maxEvenConjunction2 resultado: 134217726 duración 81.0047ms

umbral máximo: 268435456
maxEvenDividing resultado: 268435446 duración 111.0064ms
maxEvenDividing2 resultado: 268435446 duración 79.0045ms
maxEvenConjunction resultado: 268435446 duración 114.0065ms
maxEvenConjunction2 resultado: 268435446 duración 79.0045ms

umbral máximo: 536870912
maxEvenDividing resultado: 536870910 duración 107.0062ms
maxEvenDividing2 resultado: 536870910 duración 76.0043ms
resultado de maxEvenConjunction: 536870910 duración 109.0062ms
resultado de maxEvenConjunction2: 536870910 duración 80.0046ms

No encontré una explicación clara de por qué el compilador Go no optimiza el código y siempre verifica la segunda condición, incluso si la primera es falsa. ¿O será que simplemente mis ojos están "acostumbrados" y no veo algún error obvio? ¿O debería especificar algunas instrucciones especiales al compilador? Agradecería comentarios útiles.

PD: Sí, por curiosidad, ejecuté pruebas similares en Java 5 y Java 7/8 — todo claro, el tiempo de ejecución es el mismo.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster