A Go körülményei és furcsaságaik

Ön szerint ez a két lehetőség a hurkon belüli feltételek tesztelésére egyenértékű teljesítményben?

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


Az egész az „agy bemelegítésével” kezdődött; példát kellett adnom a legnagyobb páros szám optimális keresésére egy egész számokból álló [-x....x] tömbben. Azon tűnődtem, mennyivel jobb a teljesítmény, ha logikai szorzást 1-gyel használnék annak kiderítésére, hogy egy szám páros-e vagy sem.


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

Programozási tapasztalatom a Go-ban nem túl kiterjedt, alig több mint másfél éve, használtam, bár gyakran, de pusztán haszonelvű célokra (na jó, talán egy nagy terhelésű http szolgáltatáshoz kapcsolódó projektet leszámítva), így azzal kezdődött. Nyissa meg a GoLandet, és írjon egy egyszerű tesztet


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
}

Olyan eredményt kapunk, amely azt mutatja, hogy minél magasabb a küszöb, annál gyakrabban jelentkezik a teljesítmény ingadozása.

Hasonlítsa összemax 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

Jól látható, hogy ebben az esetben a különböző küszöbértékekhez különböző tesztadatokkal rendelkezünk, a processzor terhelése (i5-2540M laptopomon) 20..30% körül mozog, a GoLandről futó alkalmazás által elfoglalt memória átlagosan körülbelül 813 MB - ez az eredmény megbízhatóságát is befolyásolja, a teszteseteket lemezre kell mentenie, és az összes tesztet az egyes küszöbértékekhez egymástól elkülönítve kell futtatnia.

És most azon gondolkozva, hogyan lehet mindezt minimális költséggel megvalósítani, automatikusan kijavítom az állapotellenőrzést

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

on

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

Megint lefuttatom a teszteket... és nem értek semmit :)

A végrehajtásra fordított idő már nem százalékban/százalékban kezd eltérni, hanem 10...15%-kal.Gyorsan hozzáadok még 2 tesztet:

		
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
}

Futtatom és ezt a képet kapom:kezdeti tömbkapacitás: 100000000

maximális küszöb: 128
maxEvenDividing eredmény: 126 időtartam 116.0066 ms
maxEvenDividing2 eredmény: 126 időtartam 79.0045 ms
maxEvenConjunction eredmény: 126 időtartam 114.0065 ms
maxEvenConjunction2 eredmény: 126 időtartam 83.0048 ms

maximális küszöb: 256
maxEvenDividing eredmény: 254 időtartam 111.0063 ms
maxEvenDividing2 eredmény: 254 időtartam 77.0044 ms
maxEvenConjunction eredmény: 254 időtartam 110.0063 ms
maxEvenConjunction2 eredmény: 254 időtartam 80.0046 ms

maximális küszöb: 512
maxEvenDividing eredmény: 510 időtartam 114.0066 ms
maxEvenDividing2 eredmény: 510 időtartam 80.0045 ms
maxEvenConjunction eredmény: 510 időtartam 110.0063 ms
maxEvenConjunction2 eredmény: 510 időtartam 80.0046 ms

maximális küszöb: 1024
maxEvenDividing eredmény: 1022 időtartam 109.0063 ms
maxEvenDividing2 eredmény: 1022 időtartam 77.0044 ms
maxEvenConjunction eredmény: 1022 időtartam 111.0063 ms
maxEvenConjunction2 eredmény: 1022 időtartam 81.0047 ms

maximális küszöb: 2048
maxEvenDividing eredmény: 2046 időtartam 114.0065 ms
maxEvenDividing2 eredmény: 2046 időtartam 79.0045 ms
maxEvenConjunction eredmény: 2046 időtartam 113.0065 ms
maxEvenConjunction2 eredmény: 2046 időtartam 81.0046 ms

maximális küszöb: 4096
maxEvenDividing eredmény: 4094 időtartam 114.0065 ms
maxEvenDividing2 eredmény: 4094 időtartam 80.0046 ms
maxEvenConjunction eredmény: 4094 időtartam 111.0063 ms
maxEvenConjunction2 eredmény: 4094 időtartam 78.0045 ms

maximális küszöb: 8192
maxEvenDividing eredmény: 8190 időtartam 107.0062 ms
maxEvenDividing2 eredmény: 8190 időtartam 77.0044 ms
maxEvenConjunction eredmény: 8190 időtartam 111.0063 ms
maxEvenConjunction2 eredmény: 8190 időtartam 77.0044 ms

maximális küszöb: 16384
maxEvenDividing eredmény: 16382 időtartam 109.0063 ms
maxEvenDividing2 eredmény: 16382 időtartam 77.0044 ms
maxEvenConjunction eredmény: 16382 időtartam 108.0062 ms
maxEvenConjunction2 eredmény: 16382 időtartam 77.0044 ms

maximális küszöb: 32768
maxEvenDividing eredmény: 32766 időtartam 112.0064 ms
maxEvenDividing2 eredmény: 32766 időtartam 77.0044 ms
maxEvenConjunction eredmény: 32766 időtartam 109.0062 ms
maxEvenConjunction2 eredmény: 32766 időtartam 78.0045 ms

maximális küszöb: 65536
maxEvenDividing eredmény: 65534 időtartam 109.0062 ms
maxEvenDividing2 eredmény: 65534 időtartam 75.0043 ms
maxEvenConjunction eredmény: 65534 időtartam 109.0063 ms
maxEvenConjunction2 eredmény: 65534 időtartam 79.0045 ms

maximális küszöb: 131072
maxEvenDividing eredmény: 131070 időtartam 108.0061 ms
maxEvenDividing2 eredmény: 131070 időtartam 76.0044 ms
maxEvenConjunction eredmény: 131070 időtartam 110.0063 ms
maxEvenConjunction2 eredmény: 131070 időtartam 80.0046 ms

maximális küszöb: 262144
maxEvenDividing eredmény: 262142 időtartam 110.0063 ms
maxEvenDividing2 eredmény: 262142 időtartam 76.0044 ms
maxEvenConjunction eredmény: 262142 időtartam 107.0061 ms
maxEvenConjunction2 eredmény: 262142 időtartam 78.0044 ms

maximális küszöb: 524288
maxEvenDividing eredmény: 524286 időtartam 109.0062 ms
maxEvenDividing2 eredmény: 524286 időtartam 78.0045 ms
maxEvenConjunction eredmény: 524286 időtartam 109.0062 ms
maxEvenConjunction2 eredmény: 524286 időtartam 80.0046 ms

maximális küszöb: 1048576
maxEvenDividing eredmény: 1048574 időtartam 109.0063 ms
maxEvenDividing2 eredmény: 1048574 időtartam 80.0045 ms
maxEvenConjunction eredmény: 1048574 időtartam 114.0066 ms
maxEvenConjunction2 eredmény: 1048574 időtartam 78.0044 ms

maximális küszöb: 2097152
maxEvenDividing eredmény: 2097150 időtartam 111.0064 ms
maxEvenDividing2 eredmény: 2097150 időtartam 79.0045 ms
maxEvenConjunction eredmény: 2097150 időtartam 112.0064 ms
maxEvenConjunction2 eredmény: 2097150 időtartam 77.0044 ms

maximális küszöb: 4194304
maxEvenDividing eredmény: 4194302 időtartam 111.0063 ms
maxEvenDividing2 eredmény: 4194302 időtartam 78.0045 ms
maxEvenConjunction eredmény: 4194302 időtartam 111.0063 ms
maxEvenConjunction2 eredmény: 4194302 időtartam 77.0044 ms

maximális küszöb: 8388608
maxEvenDividing eredmény: 8388606 időtartam 109.0062 ms
maxEvenDividing2 eredmény: 8388606 időtartam 78.0045 ms
maxEvenConjunction eredmény: 8388606 időtartam 114.0065 ms
maxEvenConjunction2 eredmény: 8388606 időtartam 78.0045 ms

maximális küszöb: 16777216
maxEvenDividing eredmény: 16777214 időtartam 109.0062 ms
maxEvenDividing2 eredmény: 16777214 időtartam 77.0044 ms
maxEvenConjunction eredmény: 16777214 időtartam 109.0063 ms
maxEvenConjunction2 eredmény: 16777214 időtartam 77.0044 ms

maximális küszöb: 33554432
maxEvenDividing eredmény: 33554430 időtartam 113.0065 ms
maxEvenDividing2 eredmény: 33554430 időtartam 78.0045 ms
maxEvenConjunction eredmény: 33554430 időtartam 110.0063 ms
maxEvenConjunction2 eredmény: 33554430 időtartam 80.0045 ms

maximális küszöb: 67108864
maxEvenDividing eredmény: 67108860 időtartam 112.0064 ms
maxEvenDividing2 eredmény: 67108860 időtartam 77.0044 ms
maxEvenConjunction eredmény: 67108860 időtartam 112.0064 ms
maxEvenConjunction2 eredmény: 67108860 időtartam 80.0046 ms

maximális küszöb: 134217728
maxEvenDividing eredmény: 134217726 időtartam 109.0063 ms
maxEvenDividing2 eredmény: 134217726 időtartam 78.0044 ms
maxEvenConjunction eredmény: 134217726 időtartam 114.0065 ms
maxEvenConjunction2 eredmény: 134217726 időtartam 81.0047 ms

maximális küszöb: 268435456
maxEvenDividing eredmény: 268435446 időtartam 111.0064 ms
maxEvenDividing2 eredmény: 268435446 időtartam 79.0045 ms
maxEvenConjunction eredmény: 268435446 időtartam 114.0065 ms
maxEvenConjunction2 eredmény: 268435446 időtartam 79.0045 ms

maximális küszöb: 536870912
maxEvenDividing eredmény: 536870910 időtartam 107.0062 ms
maxEvenDividing2 eredmény: 536870910 időtartam 76.0043 ms
maxEvenConjunction eredmény: 536870910 időtartam 109.0062 ms
maxEvenConjunction2 eredmény: 536870910 időtartam 80.0046 ms

Nem találtam egyértelmű magyarázatot arra, hogy a Go fordító miért nem optimalizálja a kódot, és mindig ellenőrzi a második feltételt, még akkor is, ha az első hamis. Vagy talán csak homályos a szemem, és nem látok nyilvánvaló hibát? Vagy valami speciális utasítást kell adnia a fordítónak? Örülnék értelmes hozzászólásoknak.

PS: Igen, szórakozásból hasonló teszteket futtattam Java 5-ön és Java 7/8-on - minden világos, a végrehajtási idő ugyanaz.

Forrás: will.com

Hozzászólás