Ir condiciones y sus rarezas

¿Crees que estas dos opciones para verificar las condiciones dentro de un bucle son equivalentes en rendimiento?

if a > b && c*2 > d { .... } //  if a <= b { continue; } if c*2 > d { .... } 

Todo comenzó con un "calentamiento para el cerebro", fue necesario dar un ejemplo de una búsqueda óptima de una matriz de enteros [-x ... x] del mayor número par. Me preguntaba cuánto sería un rendimiento más alto si, para calcular un número par o no, utilizara la multiplicación lógica por 1.

 //       0 value & 1 == 0 //vs   value % 2 == 0 

Mi experiencia de programación en Go no es muy grande, solo un año y medio, lo usé, aunque a menudo, pero con fines puramente utilitarios (bueno, tal vez a excepción de un proyecto relacionado con un servicio http altamente cargado), así que comencé con él. Abre GoLand y escribe una prueba simple

 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("initial array capacity: " + fmt.Sprint(size)) var maxValue int32 //       //  .   ,   //       //   ,      for maxValue = 128; maxValue < math.MaxInt32/2+1; maxValue = maxValue * 2 { test(maxValue) } } func test(maxValue int32) { log.Println("max threshold: " + fmt.Sprint(maxValue)) arr := make([]int32, size) for i := range arr { arr[i] = rand.Int31n(maxValue) //         sign := rand.Intn(2) if sign == 1 { arr[i] = -arr[i] } } //   "  " result := maxEvenDividing("maxEvenDividing", arr) log.Printf(result.Name+"\t result: "+fmt.Sprint(result.Value)+"\t\tduration %s", result.Duration) //   "" result = maxEvenConjunction("maxEvenConjunction", arr) log.Printf(result.Name+"\t result: "+fmt.Sprint(result.Value)+"\t\tduration %s", result.Duration) } func maxEvenDividing(name string, arr []int32) Result { start := time.Now() var current int32 = math.MinInt32 for _, value := range arr { if value > 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 cuanto más alto es el umbral, más a menudo aparecen fluctuaciones en términos de rendimiento.

Comparar
max threshold: 128
maxEvenDividing result: 126 duration 116.0067ms
maxEvenConjunction result: 126 duration 116.0066ms

max threshold: 16384
maxEvenDividing result: 16382 duration 115.0066ms
maxEvenConjunction result: 16382 duration 111.0064ms

......

max threshold: 8388608
maxEvenDividing result: 8388606 duration 109.0063ms
maxEvenConjunction result: 8388606 duration 109.0062ms

max threshold: 16777216
maxEvenDividing result: 16777214 duration 108.0062ms
maxEvenConjunction result: 16777214 duration 109.0062ms

max threshold: 33554432
maxEvenDividing result: 33554430 duration 114.0066ms
maxEvenConjunction result: 33554430 duration 110.0063ms

max threshold: 67108864
maxEvenDividing result: 67108860 duration 111.0064ms
maxEvenConjunction result: 67108860 duration 109.0062ms

max threshold: 134217728
maxEvenDividing result: 134217726 duration 108.0062ms
maxEvenConjunction result: 134217726 duration 109.0063ms

max threshold: 268435456
maxEvenDividing result: 268435446 duration 111.0063ms
maxEvenConjunction result: 268435446 duration 110.0063ms

Está claro que en este caso, para diferentes umbrales tenemos diferentes conjuntos de datos de prueba, la carga del procesador (en mi computadora portátil i5-2540M) varía alrededor del 20..30%, la memoria ocupada por la aplicación que se ejecuta bajo GoLand promedia aproximadamente 813 MB, esto también es afecta la confiabilidad del resultado, debe implementar la preservación de los conjuntos de pruebas en el disco y ejecutar todas las pruebas para cada umbral de forma aislada.

Y pensando en cómo implementar todo esto a un costo mínimo, corrijo automáticamente la verificación de condición

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

en

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

Ejecuté las pruebas nuevamente ... y dejo de entender algo :)

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

 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 } 

Lanzo y obtengo esta imagen:
capacidad de matriz inicial: 100000000

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

umbral máximo: 67108864
maxEvenDividing result: 67108860 duración 112.0064ms
maxEvenDividing2 resultado: 67108860 duración 77.0044ms
maxEvenConjunction resultado: 67108860 duración 112.0064ms
Resultado maxEvenConjunction2: 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
Resultado maxEvenConjunction2: 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
Resultado maxEvenConjunction2: 268435446 duración 79.0045ms

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

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, no la encontré. ¿O tal vez mi ojo simplemente está "borroso" y no veo ningún error obvio? ¿O necesita especificar algunas instrucciones especiales para el compilador? Estaría contento con los comentarios razonables.

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

Source: https://habr.com/ru/post/444182/


All Articles