Nosacījumi Go un to dīvainības

Kā jūs domājat, vai šie divi nosacījumu pārbaudes varianti iekš cikla ir līdzvērtīgi veiktspējā?

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


Viss sākās ar "sīku smadzeņu treniņu", bija jāsniedz piemērs optimālai meklēšanai masīvā veselu skaitļu [-x….x] pēc lielākā pāra skaitļa. Man kļuva interesanti, cik daudz veiktspēja pieaugs, ja, lai noskaidrotu, vai skaitlis ir pāra vai nē, izmantos loģisko reizināšanu ar 1.


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

Mans programmēšanas pieredze ar Go nav ļoti liela, nedaudz vairāk par pusotru gadu, es to izmantoju, lai gan bieži, bet tīri utilitārajiem mērķiem (nu, varbūt izņemot vienu projektu, kas saistīts ar augstslodzes http servisu), tāpēc es tieši ar to arī sāku. Atveram GoLand un rakstām vienkāršu testu.


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("sākotnējā masīva kapacitāte: " + fmt.Sprint(size))
	var maxValue int32
        // Mēs variēsim skaitļu diapazonu no minimālā 
        // līdz maksimālajam. Jo mazāks diapazons, jo vairāk 
        // procesora laika tiks patērēts šī 
        // skaitļa salīdzināšanai, ar iepriekš atrasto un otrādi
	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
}

Saņemam rezultātu, kurā redzams, ka jo lielāks slieksnis, jo biežāk rodas fluktuācijas attiecībā uz veiktspēju.

Salīdzinietmaksimālā sliekšņa vērtība: 128
maxEvenDividing rezultāts: 126 ilgums 116.0067ms
maxEvenConjunction rezultāts: 126 ilgums 116.0066ms

maksimālais slieksnis: 16384
maxEvenDividing rezultāts: 16382 ilgums 115.0066ms
maxEvenConjunction rezultāts: 16382 ilgums 111.0064ms

......

maksimālais slieksnis: 8388608
maxEvenDividing rezultāts: 8388606 ilgums 109.0063ms
maxEvenConjunction rezultāts: 8388606 ilgums 109.0062ms

maksimālais slieksnis: 16777216
maxEvenDividing rezultāts: 16777214 ilgums 108.0062ms
maxEvenConjunction rezultāts: 16777214 ilgums 109.0062ms

maksimālais slieksnis: 33554432
maxEvenDividing rezultāts: 33554430 ilgums 114.0066ms
maxEvenConjunction rezultāts: 33554430 ilgums 110.0063ms

maksimālais slieksnis: 67108864
maxEvenDividing rezultāts: 67108860 ilgums 111.0064ms
maxEvenConjunction rezultāts: 67108860 ilgums 109.0062ms

maksimālais slieksnis: 134217728
maxEvenDividing rezultāts: 134217726 ilgums 108.0062ms
maxEvenConjunction rezultāts: 134217726 ilgums 109.0063ms

maksimālais slieksnis: 268435456
maxEvenDividing rezultāts: 268435446 ilgums 111.0063ms
maxEvenConjunction rezultāts: 268435446 ilgums 110.0063ms

Paskaidrojams, ka šajā gadījumā atšķirīgajiem sliekšņiem ir atšķirīgi testēšanas datu kopumi, procesora slodze (manā klēpjdatorā i5-2540M) svārstās ap 20..30%, atmiņa, ko patērē lietotne, palaižot to no GoLand, vidēji ir ap 813MB — tas arī ietekmē rezultāta ticamību, nepieciešams īstenot testēšanas datu kopu saglabāšanu diskā un izpildīt visus testus katram sliekšņa variantam izolēti viens no otra.

Un domājot par to, kā minimālās izmaksās to īstenot, es automātiski laboju nosacījuma pārbaudi

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

uz

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

es palaidu testus vēlreiz… un pārtraucu saprast, kas notiek 🙂

Izpildes laiks sāk atšķirties ne tikai par procentiem / daļām procenta, bet par 10..15%. Ātri pievienoju vēl 2 testus:

		
func maxEvenDividing2(name string, arr []int32) Rezultāts {
	start := time.Now()
	var current int32 = math.MinInt32
	for _, value := range arr {
		ja value <= current {
			continue
		}

		ja value%2 == 0 {
			current = value
		}
	}
	duration := time.Since(start)
	result := Rezultāts{name, duration, current}
	return result
}

func maxEvenConjunction2(name string, arr []int32) Rezultāts {
	start := time.Now()
	var current int32 = math.MinInt32
	for _, value := range arr {
		ja value <= current {
			continue
		}
		ja value&1 == 0 {
			current = value
		}
	}
	duration := time.Since(start)
	result := Rezultāts{name, duration, current}
	return result
}

es palaidu un saņemu šādu attēlu:sākotnējā masīva jauda: 100000000

maksimālā sliekšņa vērtība: 128
maxEvenDividing rezultāts: 126 ilgums 116.0066ms
maxEvenDividing2 rezultāts: 126 ilgums 79.0045ms
maxEvenConjunction rezultāts: 126 ilgums 114.0065ms
maxEvenConjunction2 rezultāts: 126 ilgums 83.0048ms

maksimālais slieksnis: 256
maxEvenDividing rezultāts: 254 ilgums 111.0063ms
maxEvenDividing2 rezultāts: 254 ilgums 77.0044ms
maxEvenConjunction rezultāts: 254 ilgums 110.0063ms
maxEvenConjunction2 rezultāts: 254 ilgums 80.0046ms

maksimālais slieksnis: 512
maxEvenDividing rezultāts: 510 ilgums 114.0066ms
maxEvenDividing2 rezultāts: 510 ilgums 80.0045ms
maxEvenConjunction rezultāts: 510 ilgums 110.0063ms
maxEvenConjunction2 rezultāts: 510 ilgums 80.0046ms

maksimālais slieksnis: 1024
maxEvenDividing rezultāts: 1022 ilgums 109.0063ms
maxEvenDividing2 rezultāts: 1022 ilgums 77.0044ms
maxEvenConjunction rezultāts: 1022 ilgums 111.0063ms
maxEvenConjunction2 rezultāts: 1022 ilgums 81.0047ms

maksimālais slieksnis: 2048
maxEvenDividing rezultāts: 2046 ilgums 114.0065ms
maxEvenDividing2 rezultāts: 2046 ilgums 79.0045ms
maxEvenConjunction rezultāts: 2046 ilgums 113.0065ms
maxEvenConjunction2 rezultāts: 2046 ilgums 81.0046ms

maksimālā robeža: 4096
maxEvenDividing rezultāts: 4094 ilgums 114.0065ms
maxEvenDividing2 rezultāts: 4094 ilgums 80.0046ms
maxEvenConjunction rezultāts: 4094 ilgums 111.0063ms
maxEvenConjunction2 rezultāts: 4094 ilgums 78.0045ms

maksimālā robeža: 8192
maxEvenDividing rezultāts: 8190 ilgums 107.0062ms
maxEvenDividing2 rezultāts: 8190 ilgums 77.0044ms
maxEvenConjunction rezultāts: 8190 ilgums 111.0063ms
maxEvenConjunction2 rezultāts: 8190 ilgums 77.0044ms

maksimālais slieksnis: 16384
maxEvenDividing rezultāts: 16382 ilgums 109.0063ms
maxEvenDividing2 rezultāts: 16382 ilgums 77.0044ms
maxEvenConjunction rezultāts: 16382 ilgums 108.0062ms
maxEvenConjunction2 rezultāts: 16382 ilgums 77.0044ms

maksimālā robeža: 32768
maxEvenDividing rezultāts: 32766 ilgums 112.0064ms
maxEvenDividing2 rezultāts: 32766 ilgums 77.0044ms
maxEvenConjunction rezultāts: 32766 ilgums 109.0062ms
maxEvenConjunction2 rezultāts: 32766 ilgums 78.0045ms

maksimālā robeža: 65536
maxEvenDividing rezultāts: 65534 ilgums 109.0062ms
maxEvenDividing2 rezultāts: 65534 ilgums 75.0043ms
maxEvenConjunction rezultāts: 65534 ilgums 109.0063ms
maxEvenConjunction2 rezultāts: 65534 ilgums 79.0045ms

maksimālā robeža: 131072
maxEvenDividing rezultāts: 131070 ilgums 108.0061ms
maxEvenDividing2 rezultāts: 131070 ilgums 76.0044ms
maxEvenConjunction rezultāts: 131070 ilgums 110.0063ms
maxEvenConjunction2 rezultāts: 131070 ilgums 80.0046ms

maksimālā robeža: 262144
maxEvenDividing rezultāts: 262142 ilgums 110.0063ms
maxEvenDividing2 rezultāts: 262142 ilgums 76.0044ms
maxEvenConjunction rezultāts: 262142 ilgums 107.0061ms
maxEvenConjunction2 rezultāts: 262142 ilgums 78.0044ms

maksimālā robeža: 524288
maxEvenDividing rezultāts: 524286 ilgums 109.0062ms
maxEvenDividing2 rezultāts: 524286 ilgums 78.0045ms
maxEvenConjunction rezultāts: 524286 ilgums 109.0062ms
maxEvenConjunction2 rezultāts: 524286 ilgums 80.0046ms

maksimālā robeža: 1048576
maxEvenDividing rezultāts: 1048574 ilgums 109.0063ms
maxEvenDividing2 rezultāts: 1048574 ilgums 80.0045ms
maxEvenConjunction rezultāts: 1048574 ilgums 114.0066ms
maxEvenConjunction2 rezultāts: 1048574 ilgums 78.0044ms

maksimālā robeža: 2097152
maxEvenDividing rezultāts: 2097150 ilgums 111.0064ms
maxEvenDividing2 rezultāts: 2097150 ilgums 79.0045ms
maxEvenConjunction rezultāts: 2097150 ilgums 112.0064ms
maxEvenConjunction2 rezultāts: 2097150 ilgums 77.0044ms

maksimālā robeža: 4194304
maxEvenDividing rezultāts: 4194302 ilgums 111.0063ms
maxEvenDividing2 rezultāts: 4194302 ilgums 78.0045ms
maxEvenConjunction rezultāts: 4194302 ilgums 111.0063ms
maxEvenConjunction2 rezultāts: 4194302 ilgums 77.0044ms

maksimālais slieksnis: 8388608
maxEvenDividing rezultāts: 8388606 ilgums 109.0062ms
maxEvenDividing2 rezultāts: 8388606 ilgums 78.0045ms
maxEvenConjunction rezultāts: 8388606 ilgums 114.0065ms
maxEvenConjunction2 rezultāts: 8388606 ilgums 78.0045ms

maksimālais slieksnis: 16777216
maxEvenDividing rezultāts: 16777214 ilgums 109.0062ms
maxEvenDividing2 rezultāts: 16777214 ilgums 77.0044ms
maxEvenConjunction rezultāts: 16777214 ilgums 109.0063ms
maxEvenConjunction2 rezultāts: 16777214 ilgums 77.0044ms

maksimālais slieksnis: 33554432
maxEvenDividing rezultāts: 33554430 ilgums 113.0065ms
maxEvenDividing2 rezultāts: 33554430 ilgums 78.0045ms
maxEvenConjunction rezultāts: 33554430 ilgums 110.0063ms
maxEvenConjunction2 rezultāts: 33554430 ilgums 80.0045ms

maksimālais slieksnis: 67108864
maxEvenDividing rezultāts: 67108860 ilgums 112.0064ms
maxEvenDividing2 rezultāts: 67108860 ilgums 77.0044ms
maxEvenConjunction rezultāts: 67108860 ilgums 112.0064ms
maxEvenConjunction2 rezultāts: 67108860 ilgums 80.0046ms

maksimālais slieksnis: 134217728
maxEvenDividing rezultāts: 134217726 ilgums 109.0063ms
maxEvenDividing2 rezultāts: 134217726 ilgums 78.0044ms
maxEvenConjunction rezultāts: 134217726 ilgums 114.0065ms
maxEvenConjunction2 rezultāts: 134217726 ilgums 81.0047ms

maksimālais slieksnis: 268435456
maxEvenDividing rezultāts: 268435446 ilgums 111.0064ms
maxEvenDividing2 rezultāts: 268435446 ilgums 79.0045ms
maxEvenConjunction rezultāts: 268435446 ilgums 114.0065ms
maxEvenConjunction2 rezultāts: 268435446 ilgums 79.0045ms

maksimālā robeža: 536870912
maxEvenDividing rezultāts: 536870910 ilgums 107.0062ms
maxEvenDividing2 rezultāts: 536870910 ilgums 76.0043ms
maxEvenConjunction rezultāts: 536870910 ilgums 109.0062ms
maxEvenConjunction2 rezultāts: 536870910 ilgums 80.0046ms

Es neatradu saprotamu skaidrojumu, kāpēc Go kompilators neoptimizē kodu un vienmēr pārbauda otro nosacījumu, pat ja pirmais ir nepaties. Varbūt mans acs ir "apmācies", un es neredzu kādu acīmredzamu kļūdu? Vai varbūt ir jānorāda kādas īpašas instrukcijas kompilatoram? Būtu prieks par saprotamiem komentāriem.

PS: Jā, interesanta nolūkā, es veicu līdzīgus testus Java 5 un Java 7/8 — viss ir skaidrs, izpildes laiks ir vienāds.

Avots: habr.com

Iegādājieties uzticamu tīmekļa hostingu ar DDoS aizsardzību, VPS VDS serveriem 🔥 Iegādājieties uzticamu tīmekļa hostingu ar DDoS aizsardzību, VPS VDS serveriem | ProHoster