Kushtet në Go dhe çuditshmëria e tyre

A mendoni se këto dy opsione për testimin e kushteve brenda një cikli janë ekuivalente në performancë?

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


Gjithçka filloi me një "ngrohje të trurit"; më duhej të jepja një shembull të një kërkimi optimal për numrin më të madh çift në një grup numrash të plotë [-x...x]. Po pyesja veten se sa performancë më e mirë do të ishte nëse do të përdorja shumëzimin logjik me 1 për të kuptuar nëse një numër është çift apo jo.


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

Përvoja ime e programimit në Go nuk është shumë e gjerë, pak më shumë se një vit e gjysmë, e përdora atë, megjithëse shpesh, por thjesht për qëllime utilitare (epo, ndoshta përveç një projekti që lidhet me një shërbim http me ngarkesë të lartë), kështu që unë filloi me të. Hapni GoLand dhe shkruani një test të thjeshtë


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
}

Marrim një rezultat që tregon se sa më i lartë të jetë pragu, aq më shpesh shfaqen luhatjet në performancë.

krahasimmax 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

Është e qartë se në këtë rast, për pragje të ndryshme kemi grupe të ndryshme të dhënash testimi, ngarkesa e procesorit (në laptopin tim i5-2540M) varion rreth 20..30%, memoria e zënë nga aplikacioni që ekzekutohet nga GoLand është mesatarisht rreth 813 MB - kjo gjithashtu ndikon në besueshmërinë e rezultatit, ju duhet të ruani rastet e provës në disk dhe të ekzekutoni të gjitha testet për secilin prag të izoluar nga njëri-tjetri.

Dhe tani, duke menduar se si t'i zbatoj të gjitha këto me kosto minimale, unë korrigjoj automatikisht kontrollin e gjendjes

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

mbi

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

I bëj përsëri testet... dhe nuk kuptoj asgjë :)

Koha e shpenzuar në ekzekutim fillon të ndryshojë jo më në përqindje/fraksione të përqindjes, por me 10..15%. Unë shpejt shtoj edhe 2 teste të tjera:

		
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
}

Unë e drejtoj atë dhe marr këtë foto:Kapaciteti fillestar i grupit: 100000000

pragu maksimal: 128
Rezultati maxEvenDividing: 126 kohëzgjatje 116.0066ms
rezultati maxEvenDividing2: 126 kohëzgjatje 79.0045 ms
Rezultati maxEvenConjunction: 126 kohëzgjatje 114.0065ms
rezultati maxEvenConjunction2: 126 kohëzgjatje 83.0048ms

pragu maksimal: 256
Rezultati maxEvenDividing: 254 kohëzgjatje 111.0063ms
rezultati maxEvenDividing2: 254 kohëzgjatje 77.0044 ms
Rezultati maxEvenConjunction: 254 kohëzgjatje 110.0063ms
rezultati maxEvenConjunction2: 254 kohëzgjatje 80.0046ms

pragu maksimal: 512
Rezultati maxEvenDividing: 510 kohëzgjatje 114.0066ms
rezultati maxEvenDividing2: 510 kohëzgjatje 80.0045 ms
Rezultati maxEvenConjunction: 510 kohëzgjatje 110.0063ms
rezultati maxEvenConjunction2: 510 kohëzgjatje 80.0046ms

pragu maksimal: 1024
Rezultati maxEvenDividing: 1022 kohëzgjatje 109.0063ms
rezultati maxEvenDividing2: 1022 kohëzgjatje 77.0044 ms
Rezultati maxEvenConjunction: 1022 kohëzgjatje 111.0063ms
rezultati maxEvenConjunction2: 1022 kohëzgjatje 81.0047ms

pragu maksimal: 2048
Rezultati maxEvenDividing: 2046 kohëzgjatje 114.0065ms
rezultati maxEvenDividing2: 2046 kohëzgjatje 79.0045 ms
Rezultati maxEvenConjunction: 2046 kohëzgjatje 113.0065ms
rezultati maxEvenConjunction2: 2046 kohëzgjatje 81.0046ms

pragu maksimal: 4096
Rezultati maxEvenDividing: 4094 kohëzgjatje 114.0065ms
rezultati maxEvenDividing2: 4094 kohëzgjatje 80.0046 ms
Rezultati maxEvenConjunction: 4094 kohëzgjatje 111.0063ms
rezultati maxEvenConjunction2: 4094 kohëzgjatje 78.0045ms

pragu maksimal: 8192
Rezultati maxEvenDividing: 8190 kohëzgjatje 107.0062ms
rezultati maxEvenDividing2: 8190 kohëzgjatje 77.0044 ms
Rezultati maxEvenConjunction: 8190 kohëzgjatje 111.0063ms
rezultati maxEvenConjunction2: 8190 kohëzgjatje 77.0044ms

pragu maksimal: 16384
Rezultati maxEvenDividing: 16382 kohëzgjatje 109.0063ms
rezultati maxEvenDividing2: 16382 kohëzgjatje 77.0044 ms
Rezultati maxEvenConjunction: 16382 kohëzgjatje 108.0062ms
rezultati maxEvenConjunction2: 16382 kohëzgjatje 77.0044ms

pragu maksimal: 32768
Rezultati maxEvenDividing: 32766 kohëzgjatje 112.0064ms
rezultati maxEvenDividing2: 32766 kohëzgjatje 77.0044 ms
Rezultati maxEvenConjunction: 32766 kohëzgjatje 109.0062ms
rezultati maxEvenConjunction2: 32766 kohëzgjatje 78.0045ms

pragu maksimal: 65536
Rezultati maxEvenDividing: 65534 kohëzgjatje 109.0062ms
rezultati maxEvenDividing2: 65534 kohëzgjatje 75.0043 ms
Rezultati maxEvenConjunction: 65534 kohëzgjatje 109.0063ms
rezultati maxEvenConjunction2: 65534 kohëzgjatje 79.0045ms

pragu maksimal: 131072
Rezultati maxEvenDividing: 131070 kohëzgjatje 108.0061ms
rezultati maxEvenDividing2: 131070 kohëzgjatje 76.0044 ms
Rezultati maxEvenConjunction: 131070 kohëzgjatje 110.0063ms
rezultati maxEvenConjunction2: 131070 kohëzgjatje 80.0046ms

pragu maksimal: 262144
Rezultati maxEvenDividing: 262142 kohëzgjatje 110.0063ms
rezultati maxEvenDividing2: 262142 kohëzgjatje 76.0044 ms
Rezultati maxEvenConjunction: 262142 kohëzgjatje 107.0061ms
rezultati maxEvenConjunction2: 262142 kohëzgjatje 78.0044ms

pragu maksimal: 524288
Rezultati maxEvenDividing: 524286 kohëzgjatje 109.0062ms
rezultati maxEvenDividing2: 524286 kohëzgjatje 78.0045 ms
Rezultati maxEvenConjunction: 524286 kohëzgjatje 109.0062ms
rezultati maxEvenConjunction2: 524286 kohëzgjatje 80.0046ms

pragu maksimal: 1048576
Rezultati maxEvenDividing: 1048574 kohëzgjatje 109.0063ms
rezultati maxEvenDividing2: 1048574 kohëzgjatje 80.0045 ms
Rezultati maxEvenConjunction: 1048574 kohëzgjatje 114.0066ms
rezultati maxEvenConjunction2: 1048574 kohëzgjatje 78.0044ms

pragu maksimal: 2097152
Rezultati maxEvenDividing: 2097150 kohëzgjatje 111.0064ms
rezultati maxEvenDividing2: 2097150 kohëzgjatje 79.0045 ms
Rezultati maxEvenConjunction: 2097150 kohëzgjatje 112.0064ms
rezultati maxEvenConjunction2: 2097150 kohëzgjatje 77.0044ms

pragu maksimal: 4194304
Rezultati maxEvenDividing: 4194302 kohëzgjatje 111.0063ms
rezultati maxEvenDividing2: 4194302 kohëzgjatje 78.0045 ms
Rezultati maxEvenConjunction: 4194302 kohëzgjatje 111.0063ms
rezultati maxEvenConjunction2: 4194302 kohëzgjatje 77.0044ms

pragu maksimal: 8388608
Rezultati maxEvenDividing: 8388606 kohëzgjatje 109.0062ms
rezultati maxEvenDividing2: 8388606 kohëzgjatje 78.0045 ms
Rezultati maxEvenConjunction: 8388606 kohëzgjatje 114.0065ms
rezultati maxEvenConjunction2: 8388606 kohëzgjatje 78.0045ms

pragu maksimal: 16777216
Rezultati maxEvenDividing: 16777214 kohëzgjatje 109.0062ms
rezultati maxEvenDividing2: 16777214 kohëzgjatje 77.0044 ms
Rezultati maxEvenConjunction: 16777214 kohëzgjatje 109.0063ms
rezultati maxEvenConjunction2: 16777214 kohëzgjatje 77.0044ms

pragu maksimal: 33554432
Rezultati maxEvenDividing: 33554430 kohëzgjatje 113.0065ms
rezultati maxEvenDividing2: 33554430 kohëzgjatje 78.0045 ms
Rezultati maxEvenConjunction: 33554430 kohëzgjatje 110.0063ms
rezultati maxEvenConjunction2: 33554430 kohëzgjatje 80.0045ms

pragu maksimal: 67108864
Rezultati maxEvenDividing: 67108860 kohëzgjatje 112.0064ms
rezultati maxEvenDividing2: 67108860 kohëzgjatje 77.0044 ms
Rezultati maxEvenConjunction: 67108860 kohëzgjatje 112.0064ms
rezultati maxEvenConjunction2: 67108860 kohëzgjatje 80.0046ms

pragu maksimal: 134217728
Rezultati maxEvenDividing: 134217726 kohëzgjatje 109.0063ms
rezultati maxEvenDividing2: 134217726 kohëzgjatje 78.0044 ms
Rezultati maxEvenConjunction: 134217726 kohëzgjatje 114.0065ms
rezultati maxEvenConjunction2: 134217726 kohëzgjatje 81.0047ms

pragu maksimal: 268435456
Rezultati maxEvenDividing: 268435446 kohëzgjatje 111.0064ms
rezultati maxEvenDividing2: 268435446 kohëzgjatje 79.0045 ms
Rezultati maxEvenConjunction: 268435446 kohëzgjatje 114.0065ms
rezultati maxEvenConjunction2: 268435446 kohëzgjatje 79.0045ms

pragu maksimal: 536870912
Rezultati maxEvenDividing: 536870910 kohëzgjatje 107.0062ms
rezultati maxEvenDividing2: 536870910 kohëzgjatje 76.0043 ms
Rezultati maxEvenConjunction: 536870910 kohëzgjatje 109.0062ms
rezultati maxEvenConjunction2: 536870910 kohëzgjatje 80.0046ms

Nuk mund të gjeja një shpjegim të qartë pse përpiluesi Go nuk optimizon kodin dhe kontrollon gjithmonë kushtin e dytë, edhe nëse i pari është i rremë. Apo ndoshta sytë e mi janë thjesht të turbullt dhe nuk shoh ndonjë gabim të dukshëm? Apo duhet t'i jepni disa udhëzime të veçanta përpiluesit? Do të isha i lumtur për komente të arsyeshme.

PS: Po, thjesht për argëtim, kam kryer teste të ngjashme në Java 5 dhe Java 7/8 - gjithçka është e qartë, koha e ekzekutimit është e njëjtë.

Burimi: www.habr.com

Shto një koment