Как мислите, дали тези два варианта за проверка на условия в цикъла са равностойни по производителност?
if a > b && c*2 > d {
....
}
// и
if a d {
....
}
Всичко започна с "разминка за мозъка"; трябваше да дам пример за оптимално търсене на най-голямото четно число в масив от цели числа [-x….x]. Интересуваше ме каква ще е производителността, ако за установяване дали числото е четно, използвам логическо умножение на 1.
//у четных чисел последний бит всегда равен 0
value & 1 == 0
//vs классический метод
value % 2 == 0
Моят опит с програмирането на Go не е много голям, малко над година и половина; макар да го използвах често, то най-вече за утилитарни цели (може би с изключение на един проект, свързан с високонакладен http сервис), затова започнах именно с него. Отваряме GoLand и пишем прост тест.
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("начална капацитет на масива: " + fmt.Sprint(size))
var maxValue int32
// Ще променяме диапазона на числата от минимално
// до максимално. Колкото по-малък е диапазонът, толкова повече
// време на процесора ще отнеме операцията
// за сравнение на текущото число с предишно намереното и обратно.
for maxValue = 128; maxValue Получаваме резултат, на който се вижда, че колкото по-висок е прагът, толкова по-често се появяват флуктуации в производителността.
Сравнетемаксимален праг: 128
резултат maxEvenDividing: 126 продължителност 116.0067ms
резултат maxEvenConjunction: 126 продължителност 116.0066ms
максимален праг: 16384
maxEvenDividing резултат: 16382 продължителност 115.0066ms
maxEvenConjunction резултат: 16382 продължителност 111.0064ms
......
максимален праг: 8388608
maxEvenDividing резултат: 8388606 продължителност 109.0063ms
maxEvenConjunction резултат: 8388606 времетраене 109.0062ms
максимална граница: 16777216
maxEvenDividing резултат: 16777214 времетраене 108.0062ms
maxEvenConjunction резултат: 16777214 времетраене 109.0062ms
максимална граница: 33554432
maxEvenDividing резултат: 33554430 времетраене 114.0066ms
maxEvenConjunction резултат: 33554430 времетраене 110.0063ms
максимална граница: 67108864
maxEvenDividing резултат: 67108860 времетраене 111.0064ms
maxEvenConjunction резултат: 67108860 времетраене 109.0062ms
максимална граница: 134217728
maxEvenDividing резултат: 134217726 времетраене 108.0062ms
maxEvenConjunction резултат: 134217726 времетраене 109.0063ms
максимална граница: 268435456
maxEvenDividing резултат: 268435446 времетраене 111.0063ms
maxEvenConjunction резултат: 268435446 времетраене 110.0063ms
Ясно е, че в този случай, за различни граници имаме различни набори от тестови данни, натоварването на процесора (на моя лаптоп i5-2540M) варира около 20..30%, паметта, заемана от приложението, стартирано от GoLand, средно около 813Мб — това също влияе на достоверността на резултата, необходимо е да реализираме запазването на тестовите набори на диска и да пуснем всички тестове за всяка граница изолирано един от друг.
И докато разсъждавам как да реализирам всичко това с минимални разходи, машинално поправям проверката на условията
if value > current && value&1 == 0 {
current = value
}
на
if value <= current {
continue;
}
if value&1 == 0 {
current = value
}
стартирам тестовете отново… и спирам да разбирам нещо 🙂
Времето, необходимо за изпълнение, започва да се различава не с проценти/части от процент, а с 10..15%. Бързо дописвам още 2 теста:
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
}
стартирам и получавам такава картина:начална капацитет на масива: 100000000
максимален праг: 128
maxEvenDividing резултат: 126 времетраене 116.0066ms
maxEvenDividing2 резултат: 126 времетраене 79.0045ms
maxEvenConjunction резултат: 126 времетраене 114.0065ms
maxEvenConjunction2 резултат: 126 времетраене 83.0048ms
максимална граница: 256
maxEvenDividing резултат: 254 времетраене 111.0063ms
maxEvenDividing2 резултат: 254 времетраене 77.0044ms
maxEvenConjunction резултат: 254 времетраене 110.0063ms
maxEvenConjunction2 резултат: 254 времетраене 80.0046ms
максимална граница: 512
maxEvenDividing резултат: 510 времетраене 114.0066ms
maxEvenDividing2 резултат: 510 времетраене 80.0045ms
maxEvenConjunction резултат: 510 времетраене 110.0063ms
maxEvenConjunction2 резултат: 510 времетраене 80.0046ms
максимална граница: 1024
maxEvenDividing резултат: 1022 времетраене 109.0063ms
maxEvenDividing2 резултат: 1022 времетраене 77.0044ms
maxEvenConjunction резултат: 1022 времетраене 111.0063ms
maxEvenConjunction2 резултат: 1022 времетраене 81.0047ms
максимална граница: 2048
maxEvenDividing резултат: 2046 времетраене 114.0065ms
maxEvenDividing2 резултат: 2046 времетраене 79.0045ms
maxEvenConjunction резултат: 2046 времетраене 113.0065ms
maxEvenConjunction2 резултат: 2046 продължителност 81.0046ms
максимален прагов: 4096
maxEvenDividing резултат: 4094 продължителност 114.0065ms
maxEvenDividing2 резултат: 4094 продължителност 80.0046ms
maxEvenConjunction резултат: 4094 продължителност 111.0063ms
maxEvenConjunction2 резултат: 4094 продължителност 78.0045ms
максимален прагов: 8192
maxEvenDividing резултат: 8190 продължителност 107.0062ms
maxEvenDividing2 резултат: 8190 продължителност 77.0044ms
maxEvenConjunction резултат: 8190 продължителност 111.0063ms
maxEvenConjunction2 резултат: 8190 продължителност 77.0044ms
максимален праг: 16384
maxEvenDividing резултат: 16382 продължителност 109.0063ms
maxEvenDividing2 резултат: 16382 продължителност 77.0044ms
maxEvenConjunction резултат: 16382 продължителност 108.0062ms
maxEvenConjunction2 резултат: 16382 продължителност 77.0044ms
максимален прагов: 32768
maxEvenDividing резултат: 32766 продължителност 112.0064ms
maxEvenDividing2 резултат: 32766 продължителност 77.0044ms
maxEvenConjunction резултат: 32766 продължителност 109.0062ms
maxEvenConjunction2 резултат: 32766 продължителност 78.0045ms
максимален прагов: 65536
maxEvenDividing резултат: 65534 продължителност 109.0062ms
maxEvenDividing2 резултат: 65534 продължителност 75.0043ms
maxEvenConjunction резултат: 65534 продължителност 109.0063ms
maxEvenConjunction2 резултат: 65534 продължителност 79.0045ms
максимален прагов: 131072
maxEvenDividing резултат: 131070 продължителност 108.0061ms
maxEvenDividing2 резултат: 131070 продължителност 76.0044ms
maxEvenConjunction резултат: 131070 продължителност 110.0063ms
maxEvenConjunction2 резултат: 131070 продължителност 80.0046ms
максимален прагов: 262144
maxEvenDividing резултат: 262142 продължителност 110.0063ms
maxEvenDividing2 резултат: 262142 продължителност 76.0044ms
maxEvenConjunction резултат: 262142 продължителност 107.0061ms
maxEvenConjunction2 резултат: 262142 продължителност 78.0044ms
максимален прагов: 524288
maxEvenDividing резултат: 524286 продължителност 109.0062ms
maxEvenDividing2 резултат: 524286 продължителност 78.0045ms
maxEvenConjunction резултат: 524286 продължителност 109.0062ms
maxEvenConjunction2 резултат: 524286 продължителност 80.0046ms
максимален прагов: 1048576
maxEvenDividing резултат: 1048574 продължителност 109.0063ms
maxEvenDividing2 резултат: 1048574 продължителност 80.0045ms
maxEvenConjunction резултат: 1048574 продължителност 114.0066ms
maxEvenConjunction2 резултат: 1048574 продължителност 78.0044ms
максимален прагов: 2097152
maxEvenDividing резултат: 2097150 продължителност 111.0064ms
maxEvenDividing2 резултат: 2097150 продължителност 79.0045ms
maxEvenConjunction резултат: 2097150 продължителност 112.0064ms
maxEvenConjunction2 резултат: 2097150 продължителност 77.0044ms
максимален прагов: 4194304
maxEvenDividing резултат: 4194302 продължителност 111.0063ms
maxEvenDividing2 резултат: 4194302 продължителност 78.0045ms
maxEvenConjunction резултат: 4194302 продължителност 111.0063ms
maxEvenConjunction2 резултат: 4194302 продължителност 77.0044ms
максимален праг: 8388608
maxEvenDividing резултат: 8388606 продължителност 109.0062ms
maxEvenDividing2 резултат: 8388606 продължителност 78.0045ms
maxEvenConjunction резултат: 8388606 продължителност 114.0065ms
maxEvenConjunction2 резултат: 8388606 продължителност 78.0045ms
максимална граница: 16777216
maxEvenDividing резултат: 16777214 продължителност 109.0062ms
maxEvenDividing2 резултат: 16777214 продължителност 77.0044ms
maxEvenConjunction резултат: 16777214 продължителност 109.0063ms
maxEvenConjunction2 резултат: 16777214 продължителност 77.0044ms
максимална граница: 33554432
maxEvenDividing резултат: 33554430 продължителност 113.0065ms
maxEvenDividing2 резултат: 33554430 продължителност 78.0045ms
maxEvenConjunction резултат: 33554430 времетраене 110.0063ms
maxEvenConjunction2 резултат: 33554430 продължителност 80.0045ms
максимална граница: 67108864
maxEvenDividing резултат: 67108860 продължителност 112.0064ms
maxEvenDividing2 резултат: 67108860 продължителност 77.0044ms
maxEvenConjunction резултат: 67108860 продължителност 112.0064ms
maxEvenConjunction2 резултат: 67108860 продължителност 80.0046ms
максимална граница: 134217728
maxEvenDividing резултат: 134217726 продължителност 109.0063ms
maxEvenDividing2 резултат: 134217726 продължителност 78.0044ms
maxEvenConjunction резултат: 134217726 продължителност 114.0065ms
maxEvenConjunction2 резултат: 134217726 продължителност 81.0047ms
максимална граница: 268435456
maxEvenDividing резултат: 268435446 продължителност 111.0064ms
maxEvenDividing2 резултат: 268435446 продължителност 79.0045ms
maxEvenConjunction резултат: 268435446 продължителност 114.0065ms
maxEvenConjunction2 резултат: 268435446 продължителност 79.0045ms
максимален прагов: 536870912
maxEvenDividing резултат: 536870910 продължителност 107.0062ms
maxEvenDividing2 резултат: 536870910 продължителност 76.0043ms
maxEvenConjunction резултат: 536870910 продължителност 109.0062ms
maxEvenConjunction2 резултат: 536870910 продължителност 80.0046ms
Не намерих ясно обяснение защо компилаторът Go не оптимизира кода и винаги проверява второто условие, дори ако първото е невярно — може би очите ми просто "замъгляват" и не виждам очевидна грешка? Или трябва да се дадат някакви специални инструкции на компилатора? Ще се радвам на обосновани коментари.
PS: Да, от интерес, пуснах аналогични тестове на Java 5 и Java 7/8 — всичко е точно, времето за изпълнение е еднакво.
Източник: habr.com
