Pogoji v Go in njihova nenavadnost

Kako mislite, ali su ova dva načina provjere uslova unutar petlje ekvivalentni po performansama?

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


Sve je počelo kao „zagrijavanje za mozak“, trebalo je dati primjer optimalne pretrage u nizu celih brojeva [-x….x] za najveći parni broj. Zanimalo me je koliko će performanse biti bolje ako za utvrđivanje da li je broj paran koristimo logičko množenje sa 1.


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

Moje iskustvo programiranja u Go-u nije previše veliko, nešto više od godinu i po dana, iako sam ga koristio često, isključivo u utilitarne svrhe (možda osim jednog projekta povezanog sa visoko opterećenim http servisom), zato sam počeo baš s njim. Otvorimo GoLand i napišimo jednostavno testiranje.


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("početni kapacitet niza: " + fmt.Sprint(size))
	var maxValue int32
        // Variraćemo raspon brojeva od minimalnog 
        // do maksimalnog. Što je manji raspon, više 
        // procesorskog vremena će se trošiti na operaciju 
        // poređenja trenutnog broja, sa prethodno pronađenim i obrnuto
	for maxValue = 128; maxValue  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
}

Dobijamo rezultat koji pokazuje da što je veći prag, to se sve češće javljaju fluktuacije u pogledu performansi.

Uporeditemaksimalna granica: 128
maksimalno ravnomerno deljenje rezultat: 126 trajanje 116.0067ms
maksimalna konjunkcija rezultat: 126 trajanje 116.0066ms

maksimalni prag: 16384
maxEvenDividing rezultat: 16382 trajanje 115.0066ms
maxEvenConjunction rezultat: 16382 trajanje 111.0064ms

......

maksimalni prag: 8388608
maxEvenDividing rezultat: 8388606 trajanje 109.0063ms
maxEvenConjunction rezultat: 8388606 trajanje 109.0062ms

maksimalna granica: 16777216
maxEvenDividing rezultat: 16777214 trajanje 108.0062ms
maxEvenConjunction rezultat: 16777214 trajanje 109.0062ms

maksimalna granica: 33554432
maxEvenDividing rezultat: 33554430 trajanje 114.0066ms
maxEvenConjunction rezultat: 33554430 trajanje 110.0063ms

maksimalna granica: 67108864
maxEvenDividing rezultat: 67108860 trajanje 111.0064ms
maxEvenConjunction rezultat: 67108860 trajanje 109.0062ms

maksimalna granica: 134217728
maxEvenDividing rezultat: 134217726 trajanje 108.0062ms
maxEvenConjunction rezultat: 134217726 trajanje 109.0063ms

maksimalna granica: 268435456
maxEvenDividing rezultat: 268435446 trajanje 111.0063ms
maxEvenConjunction rezultat: 268435446 trajanje 110.0063ms

Jasno je da u ovom slučaju, za različite granice imamo različite skupove testnih podataka, opterećenje procesora (na mom laptopu i5-2540M) kreće se oko 20..30%, memorija koju zauzima aplikacija pokrenuta iz GoLand-a u proseku oko 813MB — to takođe utiče na tačnost rezultata, potrebno je realizovati čuvanje testnih skupova na disku i prolazak svih testova za svaku granicu izolovano jedna od druge.

I razmišljajući o tome kako bi sve to implementirao s minimalnim troškovima, automatski popravljam proveru uslova

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

na

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

pokrećem testove još jednom… i prestajem da razumem bilo šta 🙂

Vreme koje se troši na izvršenje počinje da se razlikuje ne više u procentima/delovima procenta, već za 10..15% Brzo dodajem još 2 testa:

		
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
}

pokrećem i dobijam ovakvu sliku:početni kapacitet niza: 100000000

maksimalna granica: 128
maxEvenDividing rezultat: 126 trajanje 116.0066ms
maxEvenDividing2 rezultat: 126 trajanje 79.0045ms
maxEvenConjunction rezultat: 126 trajanje 114.0065ms
maxEvenConjunction2 rezultat: 126 trajanje 83.0048ms

maksimalna granica: 256
maxEvenDividing rezultat: 254 trajanje 111.0063ms
maxEvenDividing2 rezultat: 254 trajanje 77.0044ms
maxEvenConjunction rezultat: 254 trajanje 110.0063ms
maxEvenConjunction2 rezultat: 254 trajanje 80.0046ms

maksimalna granica: 512
maxEvenDividing rezultat: 510 trajanje 114.0066ms
maxEvenDividing2 rezultat: 510 trajanje 80.0045ms
maxEvenConjunction rezultat: 510 trajanje 110.0063ms
maxEvenConjunction2 rezultat: 510 trajanje 80.0046ms

maksimalna granica: 1024
maxEvenDividing rezultat: 1022 trajanje 109.0063ms
maxEvenDividing2 rezultat: 1022 trajanje 77.0044ms
maxEvenConjunction rezultat: 1022 trajanje 111.0063ms
maxEvenConjunction2 rezultat: 1022 trajanje 81.0047ms

maksimalna granica: 2048
maxEvenDividing rezultat: 2046 trajanje 114.0065ms
maxEvenDividing2 rezultat: 2046 trajanje 79.0045ms
maxEvenConjunction rezultat: 2046 trajanje 113.0065ms
maxEvenConjunction2 rezultat: 2046 trajanje 81.0046ms

maksimalni prag: 4096
maxEvenDividing rezultat: 4094 trajanje 114.0065ms
maxEvenDividing2 rezultat: 4094 trajanje 80.0046ms
maxEvenConjunction rezultat: 4094 trajanje 111.0063ms
maxEvenConjunction2 rezultat: 4094 trajanje 78.0045ms

maksimalni prag: 8192
maxEvenDividing rezultat: 8190 trajanje 107.0062ms
maxEvenDividing2 rezultat: 8190 trajanje 77.0044ms
maxEvenConjunction rezultat: 8190 trajanje 111.0063ms
maxEvenConjunction2 rezultat: 8190 trajanje 77.0044ms

maksimalni prag: 16384
maxEvenDividing rezultat: 16382 trajanje 109.0063ms
maxEvenDividing2 rezultat: 16382 trajanje 77.0044ms
maxEvenConjunction rezultat: 16382 trajanje 108.0062ms
maxEvenConjunction2 rezultat: 16382 trajanje 77.0044ms

maksimalni prag: 32768
maxEvenDividing rezultat: 32766 trajanje 112.0064ms
maxEvenDividing2 rezultat: 32766 trajanje 77.0044ms
maxEvenConjunction rezultat: 32766 trajanje 109.0062ms
maxEvenConjunction2 rezultat: 32766 trajanje 78.0045ms

maksimalni prag: 65536
maxEvenDividing rezultat: 65534 trajanje 109.0062ms
maxEvenDividing2 rezultat: 65534 trajanje 75.0043ms
maxEvenConjunction rezultat: 65534 trajanje 109.0063ms
maxEvenConjunction2 rezultat: 65534 trajanje 79.0045ms

maksimalni prag: 131072
maxEvenDividing rezultat: 131070 trajanje 108.0061ms
maxEvenDividing2 rezultat: 131070 trajanje 76.0044ms
maxEvenConjunction rezultat: 131070 trajanje 110.0063ms
maxEvenConjunction2 rezultat: 131070 trajanje 80.0046ms

maksimalni prag: 262144
maxEvenDividing rezultat: 262142 trajanje 110.0063ms
maxEvenDividing2 rezultat: 262142 trajanje 76.0044ms
maxEvenConjunction rezultat: 262142 trajanje 107.0061ms
maxEvenConjunction2 rezultat: 262142 trajanje 78.0044ms

maksimalni prag: 524288
maxEvenDividing rezultat: 524286 trajanje 109.0062ms
maxEvenDividing2 rezultat: 524286 trajanje 78.0045ms
maxEvenConjunction rezultat: 524286 trajanje 109.0062ms
maxEvenConjunction2 rezultat: 524286 trajanje 80.0046ms

maksimalni prag: 1048576
maxEvenDividing rezultat: 1048574 trajanje 109.0063ms
maxEvenDividing2 rezultat: 1048574 trajanje 80.0045ms
maxEvenConjunction rezultat: 1048574 trajanje 114.0066ms
maxEvenConjunction2 rezultat: 1048574 trajanje 78.0044ms

maksimalni prag: 2097152
maxEvenDividing rezultat: 2097150 trajanje 111.0064ms
maxEvenDividing2 rezultat: 2097150 trajanje 79.0045ms
maxEvenConjunction rezultat: 2097150 trajanje 112.0064ms
maxEvenConjunction2 rezultat: 2097150 trajanje 77.0044ms

maksimalni prag: 4194304
maxEvenDividing rezultat: 4194302 trajanje 111.0063ms
maxEvenDividing2 rezultat: 4194302 trajanje 78.0045ms
maxEvenConjunction rezultat: 4194302 trajanje 111.0063ms
maxEvenConjunction2 rezultat: 4194302 trajanje 77.0044ms

maksimalni prag: 8388608
maxEvenDividing rezultat: 8388606 trajanje 109.0062ms
maxEvenDividing2 rezultat: 8388606 trajanje 78.0045ms
maxEvenConjunction rezultat: 8388606 trajanje 114.0065ms
maxEvenConjunction2 rezultat: 8388606 trajanje 78.0045ms

maksimalna granica: 16777216
maxEvenDividing rezultat: 16777214 trajanje 109.0062ms
maxEvenDividing2 rezultat: 16777214 trajanje 77.0044ms
maxEvenConjunction rezultat: 16777214 trajanje 109.0063ms
maxEvenConjunction2 rezultat: 16777214 trajanje 77.0044ms

maksimalna granica: 33554432
maxEvenDividing rezultat: 33554430 trajanje 113.0065ms
maxEvenDividing2 rezultat: 33554430 trajanje 78.0045ms
maxEvenConjunction rezultat: 33554430 trajanje 110.0063ms
maxEvenConjunction2 rezultat: 33554430 trajanje 80.0045ms

maksimalna granica: 67108864
maxEvenDividing rezultat: 67108860 trajanje 112.0064ms
maxEvenDividing2 rezultat: 67108860 trajanje 77.0044ms
maxEvenConjunction rezultat: 67108860 trajanje 112.0064ms
maxEvenConjunction2 rezultat: 67108860 trajanje 80.0046ms

maksimalna granica: 134217728
maxEvenDividing rezultat: 134217726 trajanje 109.0063ms
maxEvenDividing2 rezultat: 134217726 trajanje 78.0044ms
maxEvenConjunction rezultat: 134217726 trajanje 114.0065ms
maxEvenConjunction2 rezultat: 134217726 trajanje 81.0047ms

maksimalna granica: 268435456
maxEvenDividing rezultat: 268435446 trajanje 111.0064ms
maxEvenDividing2 rezultat: 268435446 trajanje 79.0045ms
maxEvenConjunction rezultat: 268435446 trajanje 114.0065ms
maxEvenConjunction2 rezultat: 268435446 trajanje 79.0045ms

maksimalni prag: 536870912
maxEvenDividing rezultat: 536870910 trajanje 107.0062ms
maxEvenDividing2 rezultat: 536870910 trajanje 76.0043ms
maxEvenConjunction rezultat: 536870910 trajanje 109.0062ms
maxEvenConjunction2 rezultat: 536870910 trajanje 80.0046ms

Nisem našel jasnega pojasnila, zakaj Go prevajalnik ne optimizira kode in vedno preveri drugo stanje, tudi če je prvo napačno — morda so mi oči preprosto spremenile fokus in ne vidim očitne napake? Ali je potrebno v prevajalnik vnesti posebna navodila? Bil bi vesel smiselnih komentarjev.

PS: Ja, iz radovednosti sem pognal podobne teste na Java 5 in Java 7/8 — vse je jasno, čas izvajanja je enak.

Izvor: habr.com

Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki 🔥 Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki | ProHoster