Ir condiƧƵes e suas esquisitices

Você acha que essas duas opções para verificar condições dentro de um loop são equivalentes em desempenho?

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

Tudo começou com um "aquecimento para o cérebro", era necessÔrio dar um exemplo de uma pesquisa ideal por uma matriz de números inteiros [-x .... x] para o maior número par. Eu queria saber quanto desempenho seria maior se, para descobrir um número par ou não, usasse a multiplicação lógica por 1.

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

Minha experiência de programação no Go não é muito grande, apenas um ano e meio, eu a usei, embora com frequência, mas para fins puramente utilitÔrios (bem, talvez exceto por um projeto relacionado a um serviço http altamente carregado), então comecei com ele. Abra o GoLand e escreva um teste simples

 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 } 

Obtemos um resultado que mostra que, quanto mais alto o limite, mais frequentemente aparecem flutuaƧƵes em termos de desempenho.

Compare
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

Ɖ claro que, neste caso, para limites diferentes, temos conjuntos de dados de teste diferentes, a carga do processador (no meu laptop i5-2540M) varia em torno de 20 a 30%, a memória ocupada pelo aplicativo em execução no GoLand Ć© em mĆ©dia de 813 MB - isso tambĆ©m Ć© afeta a confiabilidade do resultado, Ć© necessĆ”rio implementar a preservação dos conjuntos de testes em disco e executar todos os testes para cada limite isoladamente.

E pensando em como implementar tudo isso a um custo mínimo, corrijo automaticamente a verificação da condição

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

em

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

Eu executo os testes novamente ... e paro de entender algo :)

O tempo gasto na execução não difere mais em porcentagem / fração de porcentagem, mas em 10 a 15% adiciono rapidamente mais 2 testes:

 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 } 

Eu inicio e recebo esta imagem:
capacidade inicial da matriz: 100000000

limite mƔximo: 128
resultado maxEvenDividing: 126 duração 116.0066ms
resultado maxEvenDividing2: 126 duração 79.0045ms
resultado maxEvenConjunction: 126 duração 114.0065ms
resultado maxEvenConjunction2: 126 duração 83.0048ms

limite mƔximo: 256
resultado maxEvenDividing: 254 duração 111.0063ms
resultado maxEvenDividing2: 254 duração 77.0044ms
resultado maxEvenConjunction: 254 duração 110.0063ms
resultado maxEvenConjunction2: 254 duração 80.0046ms

limite mƔximo: 512
resultado maxEvenDividing: 510 duração 114.0066ms
resultado maxEvenDividing2: 510 duração 80.0045ms
resultado maxEvenConjunction: 510 duração 110.0063ms
resultado maxEvenConjunction2: 510 duração 80.0046ms

limite mƔximo: 1024
resultado maxEvenDividing: 1022 duração 109.0063ms
resultado maxEvenDividing2: 1022 duração 77.0044ms
resultado maxEvenConjunction: 1022 duração 111.0063ms
resultado maxEvenConjunction2: 1022 duração 81.0047ms

limite mƔximo: 2048
resultado maxEvenDividing: 2046 duração 114.0065ms
resultado maxEvenDividing2: 2046 duração 79.0045ms
resultado maxEvenConjunction: 2046 duração 113.0065ms
resultado maxEvenConjunction2: 2046 duração 81.0046ms

limite mƔximo: 4096
resultado maxEvenDividing: 4094 duração 114.0065ms
resultado maxEvenDividing2: 4094 duração 80.0046ms
resultado maxEvenConjunction: 4094 duração 111.0063ms
resultado maxEvenConjunction2: 4094 duração 78.0045ms

limite mƔximo: 8192
resultado maxEvenDividing: 8190 duração 107.0062ms
resultado maxEvenDividing2: 8190 duração 77.0044ms
resultado maxEvenConjunction: 8190 duração 111.0063ms
resultado maxEvenConjunction2: 8190 duração 77.0044ms

limite mƔximo: 16384
resultado maxEvenDividing: 16382 duração 109.0063ms
resultado maxEvenDividing2: 16382 duração 77.0044ms
resultado maxEvenConjunction: 16382 duração 108.0062ms
resultado maxEvenConjunction2: 16382 duração 77.0044ms

limite mƔximo: 32768
resultado maxEvenDividing: 32766 duração 112.0064ms
resultado maxEvenDividing2: 32766 duração 77.0044ms
resultado maxEvenConjunction: 32766 duração 109.0062ms
resultado maxEvenConjunction2: 32766 duração 78.0045ms

limite mƔximo: 65536
resultado maxEvenDividing: 65534 duração 109.0062ms
resultado maxEvenDividing2: 65534 duração 75.0043ms
resultado maxEvenConjunction: 65534 duração 109.0063ms
resultado maxEvenConjunction2: 65534 duração 79.0045ms

limite mƔximo: 131072
resultado maxEvenDividing: 131070 duração 108.0061ms
resultado maxEvenDividing2: 131070 duração 76.0044ms
resultado maxEvenConjunction: 131070 duração 110.0063ms
resultado maxEvenConjunction2: 131070 duração 80.0046ms

limite mƔximo: 262144
resultado mÔximo: 262142 duração 110.0063ms
resultado maxEvenDividing2: 262142 duração 76.0044ms
resultado maxEvenConjunction: 262142 duração 107.0061ms
resultado maxEvenConjunction2: 262142 duração 78.0044ms

limite mƔximo: 524288
resultado maxEvenDividing: 524286 duração 109.0062ms
resultado maxEvenDividing2: 524286 duração 78.0045ms
resultado maxEvenConjunction: 524286 duração 109.0062ms
resultado maxEvenConjunction2: 524286 duração 80.0046ms

limite mƔximo: 1048576
resultado maxEvenDividing: 1048574 duração 109.0063ms
resultado maxEvenDividing2: 1048574 duração 80.0045ms
resultado maxEvenConjunction: 1048574 duração 114.0066ms
resultado maxEvenConjunction2: 1048574 duração 78.0044ms

limite mƔximo: 2097152
resultado maxEvenDividing: 2097150 duração 111.0064ms
resultado maxEvenDividing2: 2097150 duração 79.0045ms
resultado maxEvenConjunction: 2097150 duração 112.0064ms
resultado maxEvenConjunction2: 2097150 duração 77.0044ms

limite mƔximo: 4194304
resultado maxEvenDividing: 4194302 duração 111.0063ms
resultado maxEvenDividing2: 4194302 duração 78.0045ms
resultado maxEvenConjunction: 4194302 duração 111.0063ms
resultado maxEvenConjunction2: 4194302 duração 77.0044ms

limite mƔximo: 8388608
resultado mÔximo: 8388606 duração 109.0062ms
resultado maxEvenDividing2: 8388606 duração 78.0045ms
resultado maxEvenConjunction: 8388606 duração 114.0065ms
resultado maxEvenConjunction2: 8388606 duração 78.0045ms

limite mƔximo: 16777216
resultado maxEvenDividing: 16777214 duração 109.0062ms
resultado maxEvenDividing2: 16777214 duração 77.0044ms
resultado maxEvenConjunction: 16777214 duração 109.0063ms
resultado maxEvenConjunction2: 16777214 duração 77.0044ms

limite mƔximo: 33554432
resultado maxEvenDividing: 33554430 duração 113.0065ms
resultado maxEvenDividing2: 33554430 duração 78.0045ms
resultado maxEvenConjunction: 33554430 duração 110.0063ms
resultado maxEvenConjunction2: 33554430 duração 80.0045ms

limite mƔximo: 67108864
resultado maxEvenDividing: 67108860 duração 112.0064ms
resultado maxEvenDividing2: 67108860 duração 77.0044ms
resultado maxEvenConjunction: 67108860 duração 112.0064ms
resultado maxEvenConjunction2: 67108860 duração 80.0046ms

limite mƔximo: 134217728
resultado mÔximo: 134217726 duração 109.0063ms
resultado maxEvenDividing2: 134217726 duração 78.0044ms
resultado maxEvenConjunction: 134217726 duração 114.0065ms
resultado maxEvenConjunction2: 134217726 duração 81.0047ms

limite mƔximo: 268435456
resultado maxEvenDividing: 268435446 duração 111.0064ms
resultado maxEvenDividing2: 268435446 duração 79.0045ms
resultado maxEvenConjunction: 268435446 duração 114.0065ms
resultado maxEvenConjunction2: 268435446 duração 79.0045ms

limite mƔximo: 536870912
resultado maxEvenDividing: 536870910 duração 107.0062ms
resultado maxEvenDividing2: 536870910 duração 76.0043ms
resultado maxEvenConjunction: 536870910 duração 109.0062ms
resultado maxEvenConjunction2: 536870910 duração 80.0046ms

Uma explicação clara de por que o compilador Go não otimiza o código e sempre verifica a segunda condição, mesmo que a primeira seja falsa, não encontrei. Ou talvez meu olho esteja "embaçado" e não vejo nenhum erro óbvio? Ou você precisa especificar algumas instruções especiais para o compilador? Eu ficaria feliz com comentÔrios sensatos.

PS: Sim, por diversão, fiz testes semelhantes no Java 5 e Java 7/8 - tudo estÔ claro, o tempo de execução é o mesmo.

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


All Articles