Go keele tingimused ja nende veidrad nüansid

Kas arvestate, et need kaks tingimuste kontrollimise varianti tsüklis on tulemuslikkuse osas võrdsed?

		
if a > b && c*2 > d {
	....
}
// ja
if a  d {
 ....
}


Kõik sai alguse ajude soojendamisest; pidin tooma näite kõige optimaalsemast otsingust tervete numbrite massiivis [-x….x] suurima paarisnumbriga. Mind huvitab, kui palju parem tulemus on, kui kasutada paarisarvu määramiseks loogilist korrutamist 1-ga.


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

Minu Go programmeerimiskogemus ei ole väga suur, veidi üle pooleteise aasta; olen seda kasutanud kuigi sageli, kuid puhtalt utilitaarselt (välja arvatud võib-olla üks projekt, mis on seotud suure koormusega http teenusega), seetõttu alustasin ma just sellest. Avame GoLand'i ja kirjutame lihtsa testi.


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("algne massiivi maht: " + fmt.Sprint(size))
	var maxValue int32
        // Muudame numbrite vahemikku alates minimaalsetest 
        // maksimaalseteni. Mida väiksem on vahemik, seda rohkem 
        // protsessoriaega kulub praeguse numbri 
        // võrreldamiseks varem leitud arvuga ja vastupidi
	for maxValue = 128; maxValue < math.MaxInt32/2+1; maxValue = maxValue * 2 {
		test(maxValue)
	}
}

func test(maxValue int32) {
	log.Println("maksimaalne künnis: " + fmt.Sprint(maxValue))
	arr := make([]int32, size)
	for i := range arr {
		arr[i] = rand.Int31n(maxValue)
                // Testandmetes on meil vaja ka negatiivseid numbreid 
		sign := rand.Intn(2)
		if sign == 1 {
			arr[i] = -arr[i]
		}
	}

        // Käivitame testi "jagamine jäägiga"
	result := maxEvenDividing("maxEvenDividing", arr)
	log.Printf(result.Name+"t tulemus: "+fmt.Sprint(result.Value)+"ttkestus %s", result.Duration)

        // Käivitame testi "logaritm"
	result = maxEvenConjunction("maxEvenConjunction", arr)
	log.Printf(result.Name+"t tulemus: "+fmt.Sprint(result.Value)+"ttkestus %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
}

Tulemus näitab, et mida suurem on threshold, seda sagedamini esinevad jõudluse kõikumised.

Võrdlemaksimaalne künnis: 128
maxEvenDividing tulemus: 126 aeg 116.0067ms
maxEvenConjunction tulemus: 126 aeg 116.0066ms

max threshold: 16384
maxEvenDividing tulemus: 16382 kestus 115.0066ms
maxEvenConjunction tulemus: 16382 kestus 111.0064ms

......

max threshold: 8388608
maxEvenDividing tulemus: 8388606 kestus 109.0063ms
maxEvenConjunction tulemus: 8388606 kestus 109.0062ms

max threshold: 16777216
maxEvenDividing tulemus: 16777214 kestus 108.0062ms
maxEvenConjunction tulemus: 16777214 kestus 109.0062ms

max threshold: 33554432
maxEvenDividing tulemus: 33554430 kestus 114.0066ms
maxEvenConjunction tulemus: 33554430 kestus 110.0063ms

max threshold: 67108864
maxEvenDividing tulemus: 67108860 kestus 111.0064ms
maxEvenConjunction tulemus: 67108860 kestus 109.0062ms

max threshold: 134217728
maxEvenDividing tulemus: 134217726 kestus 108.0062ms
maxEvenConjunction tulemus: 134217726 kestus 109.0063ms

max threshold: 268435456
maxEvenDividing tulemus: 268435446 kestus 111.0063ms
maxEvenConjunction tulemus: 268435446 kestus 110.0063ms

On selge, et antud juhul, erinevate thresholdide puhul on meil erinevad testandmete kogumid, protsessorikoormus (minu sülearvutis i5-2540M) kõigub 20..30% vahel, GoLandist käivitatud rakenduse mälu kasutus on keskmiselt umbes 813Mb — see mõjutab samuti tulemuse usaldusväärsust, tuleb rakendada testigruppide salvestamine kettale ja testida iga thresholdi üksteisest eraldi.

Ja mõtlen, kuidas minimaalselt kulutustega kõike seda ellu viia, ja parandan automaatselt tingimuse kontrolli.

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

järgnevaga

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

Käitan testid uuesti... ja lõpetan kõigest arusaamise 🙂

Kasutamiseks kuluv aeg erineb enam mitte protsentidest / osaprotsentidest, vaid 10..15% võrra. Kiiresti lisan veel 2 testi:

		
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
}

Käitan ja saan sellise pildi:esialgne massiivi maht: 100000000

maksimaalne künnis: 128
maxEvenDividing tulemus: 126 kestus 116.0066ms
maxEvenDividing2 tulemus: 126 kestus 79.0045ms
maxEvenConjunction tulemus: 126 kestus 114.0065ms
maxEvenConjunction2 tulemus: 126 kestus 83.0048ms

maximaalne künnis: 256
maxEvenDividing tulemus: 254 kestus 111.0063ms
maxEvenDividing2 tulemus: 254 kestus 77.0044ms
maxEvenConjunction tulemus: 254 kestus 110.0063ms
maxEvenConjunction2 tulemus: 254 kestus 80.0046ms

maximaalne künnis: 512
maxEvenDividing tulemus: 510 kestus 114.0066ms
maxEvenDividing2 tulemus: 510 kestus 80.0045ms
maxEvenConjunction tulemus: 510 kestus 110.0063ms
maxEvenConjunction2 tulemus: 510 kestus 80.0046ms

max lävi: 1024
maxEvenDividing tulemus: 1022 kestus 109.0063ms
maxEvenDividing2 tulemus: 1022 kestus 77.0044ms
maxEvenConjunction tulemus: 1022 kestus 111.0063ms
maxEvenConjunction2 tulemus: 1022 kestus 81.0047ms

max lävi: 2048
maxEvenDividing tulemus: 2046 kestus 114.0065ms
maxEvenDividing2 tulemus: 2046 kestus 79.0045ms
maxEvenConjunction tulemus: 2046 kestus 113.0065ms
maxEvenConjunction2 tulemus: 2046 kestus 81.0046ms

max lävi: 4096
maxEvenDividing tulemus: 4094 kestus 114.0065ms
maxEvenDividing2 tulemus: 4094 kestus 80.0046ms
maxEvenConjunction tulemus: 4094 kestus 111.0063ms
maxEvenConjunction2 tulemus: 4094 kestus 78.0045ms

max lävi: 8192
maxEvenDividing tulemus: 8190 kestus 107.0062ms
maxEvenDividing2 tulemus: 8190 kestus 77.0044ms
maxEvenConjunction tulemus: 8190 kestus 111.0063ms
maxEvenConjunction2 tulemus: 8190 kestus 77.0044ms

max threshold: 16384
maxEvenDividing tulemus: 16382 kestus 109.0063ms
maxEvenDividing2 tulemus: 16382 kestus 77.0044ms
maxEvenConjunction tulemus: 16382 kestus 108.0062ms
maxEvenConjunction2 tulemus: 16382 kestus 77.0044ms

max lävi: 32768
maxEvenDividing tulemus: 32766 kestus 112.0064ms
maxEvenDividing2 tulemus: 32766 kestus 77.0044ms
maxEvenConjunction tulemus: 32766 kestus 109.0062ms
maxEvenConjunction2 tulemus: 32766 kestus 78.0045ms

max lävi: 65536
maxEvenDividing tulemus: 65534 kestus 109.0062ms
maxEvenDividing2 tulemus: 65534 kestus 75.0043ms
maxEvenConjunction tulemus: 65534 kestus 109.0063ms
maxEvenConjunction2 tulemus: 65534 kestus 79.0045ms

max lävi: 131072
maxEvenDividing tulemus: 131070 kestus 108.0061ms
maxEvenDividing2 tulemus: 131070 kestus 76.0044ms
maxEvenConjunction tulemus: 131070 kestus 110.0063ms
maxEvenConjunction2 tulemus: 131070 kestus 80.0046ms

max lävi: 262144
maxEvenDividing tulemus: 262142 kestus 110.0063ms
maxEvenDividing2 tulemus: 262142 kestus 76.0044ms
maxEvenConjunction tulemus: 262142 kestus 107.0061ms
maxEvenConjunction2 tulemus: 262142 kestus 78.0044ms

max künnis: 524288
maxEvenDividing tulemus: 524286 kestus 109.0062ms
maxEvenDividing2 tulemus: 524286 kestus 78.0045ms
maxEvenConjunction tulemus: 524286 kestus 109.0062ms
maxEvenConjunction2 tulemus: 524286 kestus 80.0046ms

max künnis: 1048576
maxEvenDividing tulemus: 1048574 kestus 109.0063ms
maxEvenDividing2 tulemus: 1048574 kestus 80.0045ms
maxEvenConjunction tulemus: 1048574 kestus 114.0066ms
maxEvenConjunction2 tulemus: 1048574 kestus 78.0044ms

max künnis: 2097152
maxEvenDividing tulemus: 2097150 kestus 111.0064ms
maxEvenDividing2 tulemus: 2097150 kestus 79.0045ms
maxEvenConjunction tulemus: 2097150 kestus 112.0064ms
maxEvenConjunction2 tulemus: 2097150 kestus 77.0044ms

max künnis: 4194304
maxEvenDividing tulemus: 4194302 kestus 111.0063ms
maxEvenDividing2 tulemus: 4194302 kestus 78.0045ms
maxEvenConjunction tulemus: 4194302 kestus 111.0063ms
maxEvenConjunction2 tulemus: 4194302 kestus 77.0044ms

max threshold: 8388608
maxEvenDividing tulemus: 8388606 kestus 109.0062ms
maxEvenDividing2 tulemus: 8388606 kestus 78.0045ms
maxEvenConjunction tulemus: 8388606 kestus 114.0065ms
maxEvenConjunction2 tulemus: 8388606 kestus 78.0045ms

max threshold: 16777216
maxEvenDividing tulemus: 16777214 kestus 109.0062ms
maxEvenDividing2 tulemus: 16777214 kestus 77.0044ms
maxEvenConjunction tulemus: 16777214 kestus 109.0063ms
maxEvenConjunction2 tulemus: 16777214 kestus 77.0044ms

max threshold: 33554432
maxEvenDividing tulemus: 33554430 kestus 113.0065ms
maxEvenDividing2 tulemus: 33554430 kestus 78.0045ms
maxEvenConjunction tulemus: 33554430 kestus 110.0063ms
maxEvenConjunction2 tulemus: 33554430 kestus 80.0045ms

max threshold: 67108864
maxEvenDividing tulemus: 67108860 kestus 112.0064ms
maxEvenDividing2 tulemus: 67108860 kestus 77.0044ms
maxEvenConjunction tulemus: 67108860 kestus 112.0064ms
maxEvenConjunction2 tulemus: 67108860 kestus 80.0046ms

max threshold: 134217728
maxEvenDividing tulemus: 134217726 kestus 109.0063ms
maxEvenDividing2 tulemus: 134217726 kestus 78.0044ms
maxEvenConjunction tulemus: 134217726 kestus 114.0065ms
maxEvenConjunction2 tulemus: 134217726 kestus 81.0047ms

max threshold: 268435456
maxEvenDividing tulemus: 268435446 kestus 111.0064ms
maxEvenDividing2 tulemus: 268435446 kestus 79.0045ms
maxEvenConjunction tulemus: 268435446 kestus 114.0065ms
maxEvenConjunction2 tulemus: 268435446 kestus 79.0045ms

max piir: 536870912
maxEvenDividing tulemus: 536870910 kestus 107.0062ms
maxEvenDividing2 tulemus: 536870910 kestus 76.0043ms
maxEvenConjunction tulemus: 536870910 kestus 109.0062ms
maxEvenConjunction2 tulemus: 536870910 kestus 80.0046ms

Mingit selget selgitust, miks Go kompilaator ei optimeeri koodi ja kontrollib alati teist tingimust, isegi kui esimene on vale — ma ei leidnud. Võib-olla on mu silmad lihtsalt 'hämaraks' muutunud ja ma ei näe mingit ilmset viga? Või pean ma kompilaatorile andma mingid erilised juhised? Oleksin tänulik arusaadavate kommentaaride eest.

PS: Jah, huvitamisest jooksin läbi sarnased testid Java 5 ja Java 7/8 - kõik sujub, töötamise aeg on sama.

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster