Cres que estas dúas opcións para probar as condicións dentro dun bucle son equivalentes en rendemento?
if a > b && c*2 > d {
....
}
// и
if a <= b {
continue;
}
if c*2 > d {
....
}
Todo comezou cun "quecemento do cerebro"; tiven que dar un exemplo dunha busca óptima para o maior número par nunha matriz de enteiros [-x....x]. Preguntábame canto sería mellor o rendemento se utilizase a multiplicación lóxica por 1 para descubrir se un número é par ou non.
//у четных чисел последний бит всегда равен 0
value & 1 == 0
//vs классический метод
value % 2 == 0
A miña experiencia de programación en Go non é moi extensa, pouco máis de ano e medio, useino, aínda que moitas veces, pero puramente con fins utilitarios (ben, quizais agás un proxecto relacionado cun servizo http de alta carga), así que eu comezou con el. Abre GoLand e escribe unha proba sinxela
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)+"ttduration %s", result.Duration)
// запускаем тест "конъюнкции"
result = maxEvenConjunction("maxEvenConjunction", arr)
log.Printf(result.Name+"t result: "+fmt.Sprint(result.Value)+"ttduration %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 un resultado que mostra que canto máis alto é o limiar, máis frecuentemente aparecen as flutuacións no rendemento.
Comparemax 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 neste caso, para diferentes limiares temos diferentes conxuntos de datos de proba, a carga do procesador (no meu portátil i5-2540M) varía arredor do 20..30%, a memoria ocupada pola aplicación que se executa dende GoLand é de media. preto de 813 MB - isto tamén afecta a fiabilidade do resultado, cómpre gardar casos de proba no disco e executar todas as probas para cada limiar illados entre si.
E agora, pensando en como implementar todo isto cuns custos mínimos, corrixo automaticamente a comprobación de condicións
if value > current && value&1 == 0 {
current = value
}
en
if value <= current {
continue;
}
if value&1 == 0 {
current = value
}
Volvo a facer as probas... e deixo de entender nada :)
O tempo dedicado á execución comeza a diferir non en porcentaxes/fraccións de porcentaxe, senón en 10..15%. Engado rapidamente 2 probas máis:
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
}
Execúoo e saco esta imaxe:Capacidade inicial da matriz: 100000000
límite máximo: 128
Resultado maxEvenDividing: 126 duración 116.0066 ms
resultado maxEvenDividing2: 126 duración 79.0045 ms
Resultado maxEvenConjunction: 126 duración 114.0065 ms
resultado maxEvenConjunction2: 126 duración 83.0048 ms
límite máximo: 256
Resultado maxEvenDividing: 254 duración 111.0063 ms
resultado maxEvenDividing2: 254 duración 77.0044 ms
Resultado maxEvenConjunction: 254 duración 110.0063 ms
resultado maxEvenConjunction2: 254 duración 80.0046 ms
límite máximo: 512
Resultado maxEvenDividing: 510 duración 114.0066 ms
resultado maxEvenDividing2: 510 duración 80.0045 ms
Resultado maxEvenConjunction: 510 duración 110.0063 ms
resultado maxEvenConjunction2: 510 duración 80.0046 ms
límite máximo: 1024
Resultado maxEvenDividing: 1022 duración 109.0063 ms
resultado maxEvenDividing2: 1022 duración 77.0044 ms
Resultado maxEvenConjunction: 1022 duración 111.0063 ms
resultado maxEvenConjunction2: 1022 duración 81.0047 ms
límite máximo: 2048
Resultado maxEvenDividing: 2046 duración 114.0065 ms
resultado maxEvenDividing2: 2046 duración 79.0045 ms
Resultado maxEvenConjunction: 2046 duración 113.0065 ms
resultado maxEvenConjunction2: 2046 duración 81.0046 ms
límite máximo: 4096
Resultado maxEvenDividing: 4094 duración 114.0065 ms
resultado maxEvenDividing2: 4094 duración 80.0046 ms
Resultado maxEvenConjunction: 4094 duración 111.0063 ms
resultado maxEvenConjunction2: 4094 duración 78.0045 ms
límite máximo: 8192
Resultado maxEvenDividing: 8190 duración 107.0062 ms
resultado maxEvenDividing2: 8190 duración 77.0044 ms
Resultado maxEvenConjunction: 8190 duración 111.0063 ms
resultado maxEvenConjunction2: 8190 duración 77.0044 ms
límite máximo: 16384
Resultado maxEvenDividing: 16382 duración 109.0063 ms
resultado maxEvenDividing2: 16382 duración 77.0044 ms
Resultado maxEvenConjunction: 16382 duración 108.0062 ms
resultado maxEvenConjunction2: 16382 duración 77.0044 ms
límite máximo: 32768
Resultado maxEvenDividing: 32766 duración 112.0064 ms
resultado maxEvenDividing2: 32766 duración 77.0044 ms
Resultado maxEvenConjunction: 32766 duración 109.0062 ms
resultado maxEvenConjunction2: 32766 duración 78.0045 ms
límite máximo: 65536
Resultado maxEvenDividing: 65534 duración 109.0062 ms
resultado maxEvenDividing2: 65534 duración 75.0043 ms
Resultado maxEvenConjunction: 65534 duración 109.0063 ms
resultado maxEvenConjunction2: 65534 duración 79.0045 ms
límite máximo: 131072
Resultado maxEvenDividing: 131070 duración 108.0061 ms
resultado maxEvenDividing2: 131070 duración 76.0044 ms
Resultado maxEvenConjunction: 131070 duración 110.0063 ms
resultado maxEvenConjunction2: 131070 duración 80.0046 ms
límite máximo: 262144
Resultado maxEvenDividing: 262142 duración 110.0063 ms
resultado maxEvenDividing2: 262142 duración 76.0044 ms
Resultado maxEvenConjunction: 262142 duración 107.0061 ms
resultado maxEvenConjunction2: 262142 duración 78.0044 ms
límite máximo: 524288
Resultado maxEvenDividing: 524286 duración 109.0062 ms
resultado maxEvenDividing2: 524286 duración 78.0045 ms
Resultado maxEvenConjunction: 524286 duración 109.0062 ms
resultado maxEvenConjunction2: 524286 duración 80.0046 ms
límite máximo: 1048576
Resultado maxEvenDividing: 1048574 duración 109.0063 ms
resultado maxEvenDividing2: 1048574 duración 80.0045 ms
Resultado maxEvenConjunction: 1048574 duración 114.0066 ms
resultado maxEvenConjunction2: 1048574 duración 78.0044 ms
límite máximo: 2097152
Resultado maxEvenDividing: 2097150 duración 111.0064 ms
resultado maxEvenDividing2: 2097150 duración 79.0045 ms
Resultado maxEvenConjunction: 2097150 duración 112.0064 ms
resultado maxEvenConjunction2: 2097150 duración 77.0044 ms
límite máximo: 4194304
Resultado maxEvenDividing: 4194302 duración 111.0063 ms
resultado maxEvenDividing2: 4194302 duración 78.0045 ms
Resultado maxEvenConjunction: 4194302 duración 111.0063 ms
resultado maxEvenConjunction2: 4194302 duración 77.0044 ms
límite máximo: 8388608
Resultado maxEvenDividing: 8388606 duración 109.0062 ms
resultado maxEvenDividing2: 8388606 duración 78.0045 ms
Resultado maxEvenConjunction: 8388606 duración 114.0065 ms
resultado maxEvenConjunction2: 8388606 duración 78.0045 ms
límite máximo: 16777216
Resultado maxEvenDividing: 16777214 duración 109.0062 ms
resultado maxEvenDividing2: 16777214 duración 77.0044 ms
Resultado maxEvenConjunction: 16777214 duración 109.0063 ms
resultado maxEvenConjunction2: 16777214 duración 77.0044 ms
límite máximo: 33554432
Resultado maxEvenDividing: 33554430 duración 113.0065 ms
resultado maxEvenDividing2: 33554430 duración 78.0045 ms
Resultado maxEvenConjunction: 33554430 duración 110.0063 ms
resultado maxEvenConjunction2: 33554430 duración 80.0045 ms
límite máximo: 67108864
Resultado maxEvenDividing: 67108860 duración 112.0064 ms
resultado maxEvenDividing2: 67108860 duración 77.0044 ms
Resultado maxEvenConjunction: 67108860 duración 112.0064 ms
resultado maxEvenConjunction2: 67108860 duración 80.0046 ms
límite máximo: 134217728
Resultado maxEvenDividing: 134217726 duración 109.0063 ms
resultado maxEvenDividing2: 134217726 duración 78.0044 ms
Resultado maxEvenConjunction: 134217726 duración 114.0065 ms
resultado maxEvenConjunction2: 134217726 duración 81.0047 ms
límite máximo: 268435456
Resultado maxEvenDividing: 268435446 duración 111.0064 ms
resultado maxEvenDividing2: 268435446 duración 79.0045 ms
Resultado maxEvenConjunction: 268435446 duración 114.0065 ms
resultado maxEvenConjunction2: 268435446 duración 79.0045 ms
límite máximo: 536870912
Resultado maxEvenDividing: 536870910 duración 107.0062 ms
resultado maxEvenDividing2: 536870910 duración 76.0043 ms
Resultado maxEvenConjunction: 536870910 duración 109.0062 ms
resultado maxEvenConjunction2: 536870910 duración 80.0046 ms
Non puiden atopar unha explicación clara de por que o compilador Go non optimiza o código e sempre verifica a segunda condición, aínda que a primeira sexa falsa. Ou quizais os meus ollos están borrosos e non vexo ningún erro evidente? Ou precisas proporcionar algunhas instrucións especiais ao compilador? Gustaríame comentarios sensatos.
PS: Si, só por diversión, realicei probas similares en Java 5 e Java 7/8: todo está claro, o tempo de execución é o mesmo.
Fonte: www.habr.com