Go'daki koşullar ve tuhaflıkları

Bir döngü içindeki koşulları test etmek için bu iki seçeneğin performans açısından eşdeğer olduğunu düşünüyor musunuz?

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


Her şey bir “beynin ısınmasıyla” başladı; [-x....x] tamsayı dizisindeki en büyük çift sayı için optimal aramaya bir örnek vermem gerekiyordu. Bir sayının çift olup olmadığını anlamak için 1 ile mantıksal çarpmayı kullanırsam performansın ne kadar iyi olacağını merak ediyordum.


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

Go'daki programlama deneyimim çok kapsamlı değil, bir buçuk yıldan biraz fazla bir süredir, onu sık sık da olsa tamamen faydacı amaçlarla kullandım (belki de yüksek yüklü http hizmetiyle ilgili bir proje hariç), bu yüzden onunla başladı. GoLand'ı açın ve basit bir test yazın


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
}

Eşik ne kadar yüksek olursa performanstaki dalgalanmaların da o kadar sık ​​ortaya çıktığını gösteren bir sonuç elde ediyoruz.

karşılaştırmakmax 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

Bu durumda, farklı eşikler için farklı test veri setlerine sahip olduğumuz, işlemci yükünün (i5-2540M dizüstü bilgisayarımda) %20..30 civarında değiştiği, GoLand'dan çalıştırılan uygulamanın kapladığı belleğin ortalama olduğu açıktır. yaklaşık 813 MB - bu aynı zamanda sonucun güvenilirliğini de etkiler; test senaryolarını diske kaydetmeniz ve her eşik için tüm testleri birbirinden ayrı olarak çalıştırmanız gerekir.

Ve şimdi tüm bunları minimum maliyetle nasıl uygulayacağımı düşünerek durum kontrolünü otomatik olarak düzeltiyorum

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

üzerinde

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

Testleri tekrar yapıyorum... ve hiçbir şey anlamıyorum :)

Uygulama için harcanan zaman artık yüzde/kesir olarak değil, %10..15 oranında farklılık göstermeye başlıyor. Hemen 2 test daha ekliyorum:

		
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
}

Çalıştırıyorum ve bu resmi alıyorum:başlangıç ​​dizi kapasitesi: 100000000

maksimum eşik: 128
maxEvenDividing sonucu: 126 süre 116.0066ms
maxEvenDividing2 sonucu: 126 süre 79.0045ms
maxEvenConjunction sonucu: 126 süre 114.0065ms
maxEvenConjunction2 sonucu: 126 süre 83.0048ms

maksimum eşik: 256
maxEvenDividing sonucu: 254 süre 111.0063ms
maxEvenDividing2 sonucu: 254 süre 77.0044ms
maxEvenConjunction sonucu: 254 süre 110.0063ms
maxEvenConjunction2 sonucu: 254 süre 80.0046ms

maksimum eşik: 512
maxEvenDividing sonucu: 510 süre 114.0066ms
maxEvenDividing2 sonucu: 510 süre 80.0045ms
maxEvenConjunction sonucu: 510 süre 110.0063ms
maxEvenConjunction2 sonucu: 510 süre 80.0046ms

maksimum eşik: 1024
maxEvenDividing sonucu: 1022 süre 109.0063ms
maxEvenDividing2 sonucu: 1022 süre 77.0044ms
maxEvenConjunction sonucu: 1022 süre 111.0063ms
maxEvenConjunction2 sonucu: 1022 süre 81.0047ms

maksimum eşik: 2048
maxEvenDividing sonucu: 2046 süre 114.0065ms
maxEvenDividing2 sonucu: 2046 süre 79.0045ms
maxEvenConjunction sonucu: 2046 süre 113.0065ms
maxEvenConjunction2 sonucu: 2046 süre 81.0046ms

maksimum eşik: 4096
maxEvenDividing sonucu: 4094 süre 114.0065ms
maxEvenDividing2 sonucu: 4094 süre 80.0046ms
maxEvenConjunction sonucu: 4094 süre 111.0063ms
maxEvenConjunction2 sonucu: 4094 süre 78.0045ms

maksimum eşik: 8192
maxEvenDividing sonucu: 8190 süre 107.0062ms
maxEvenDividing2 sonucu: 8190 süre 77.0044ms
maxEvenConjunction sonucu: 8190 süre 111.0063ms
maxEvenConjunction2 sonucu: 8190 süre 77.0044ms

maksimum eşik: 16384
maxEvenDividing sonucu: 16382 süre 109.0063ms
maxEvenDividing2 sonucu: 16382 süre 77.0044ms
maxEvenConjunction sonucu: 16382 süre 108.0062ms
maxEvenConjunction2 sonucu: 16382 süre 77.0044ms

maksimum eşik: 32768
maxEvenDividing sonucu: 32766 süre 112.0064ms
maxEvenDividing2 sonucu: 32766 süre 77.0044ms
maxEvenConjunction sonucu: 32766 süre 109.0062ms
maxEvenConjunction2 sonucu: 32766 süre 78.0045ms

maksimum eşik: 65536
maxEvenDividing sonucu: 65534 süre 109.0062ms
maxEvenDividing2 sonucu: 65534 süre 75.0043ms
maxEvenConjunction sonucu: 65534 süre 109.0063ms
maxEvenConjunction2 sonucu: 65534 süre 79.0045ms

maksimum eşik: 131072
maxEvenDividing sonucu: 131070 süre 108.0061ms
maxEvenDividing2 sonucu: 131070 süre 76.0044ms
maxEvenConjunction sonucu: 131070 süre 110.0063ms
maxEvenConjunction2 sonucu: 131070 süre 80.0046ms

maksimum eşik: 262144
maxEvenDividing sonucu: 262142 süre 110.0063ms
maxEvenDividing2 sonucu: 262142 süre 76.0044ms
maxEvenConjunction sonucu: 262142 süre 107.0061ms
maxEvenConjunction2 sonucu: 262142 süre 78.0044ms

maksimum eşik: 524288
maxEvenDividing sonucu: 524286 süre 109.0062ms
maxEvenDividing2 sonucu: 524286 süre 78.0045ms
maxEvenConjunction sonucu: 524286 süre 109.0062ms
maxEvenConjunction2 sonucu: 524286 süre 80.0046ms

maksimum eşik: 1048576
maxEvenDividing sonucu: 1048574 süre 109.0063ms
maxEvenDividing2 sonucu: 1048574 süre 80.0045ms
maxEvenConjunction sonucu: 1048574 süre 114.0066ms
maxEvenConjunction2 sonucu: 1048574 süre 78.0044ms

maksimum eşik: 2097152
maxEvenDividing sonucu: 2097150 süre 111.0064ms
maxEvenDividing2 sonucu: 2097150 süre 79.0045ms
maxEvenConjunction sonucu: 2097150 süre 112.0064ms
maxEvenConjunction2 sonucu: 2097150 süre 77.0044ms

maksimum eşik: 4194304
maxEvenDividing sonucu: 4194302 süre 111.0063ms
maxEvenDividing2 sonucu: 4194302 süre 78.0045ms
maxEvenConjunction sonucu: 4194302 süre 111.0063ms
maxEvenConjunction2 sonucu: 4194302 süre 77.0044ms

maksimum eşik: 8388608
maxEvenDividing sonucu: 8388606 süre 109.0062ms
maxEvenDividing2 sonucu: 8388606 süre 78.0045ms
maxEvenConjunction sonucu: 8388606 süre 114.0065ms
maxEvenConjunction2 sonucu: 8388606 süre 78.0045ms

maksimum eşik: 16777216
maxEvenDividing sonucu: 16777214 süre 109.0062ms
maxEvenDividing2 sonucu: 16777214 süre 77.0044ms
maxEvenConjunction sonucu: 16777214 süre 109.0063ms
maxEvenConjunction2 sonucu: 16777214 süre 77.0044ms

maksimum eşik: 33554432
maxEvenDividing sonucu: 33554430 süre 113.0065ms
maxEvenDividing2 sonucu: 33554430 süre 78.0045ms
maxEvenConjunction sonucu: 33554430 süre 110.0063ms
maxEvenConjunction2 sonucu: 33554430 süre 80.0045ms

maksimum eşik: 67108864
maxEvenDividing sonucu: 67108860 süre 112.0064ms
maxEvenDividing2 sonucu: 67108860 süre 77.0044ms
maxEvenConjunction sonucu: 67108860 süre 112.0064ms
maxEvenConjunction2 sonucu: 67108860 süre 80.0046ms

maksimum eşik: 134217728
maxEvenDividing sonucu: 134217726 süre 109.0063ms
maxEvenDividing2 sonucu: 134217726 süre 78.0044ms
maxEvenConjunction sonucu: 134217726 süre 114.0065ms
maxEvenConjunction2 sonucu: 134217726 süre 81.0047ms

maksimum eşik: 268435456
maxEvenDividing sonucu: 268435446 süre 111.0064ms
maxEvenDividing2 sonucu: 268435446 süre 79.0045ms
maxEvenConjunction sonucu: 268435446 süre 114.0065ms
maxEvenConjunction2 sonucu: 268435446 süre 79.0045ms

maksimum eşik: 536870912
maxEvenDividing sonucu: 536870910 süre 107.0062ms
maxEvenDividing2 sonucu: 536870910 süre 76.0043ms
maxEvenConjunction sonucu: 536870910 süre 109.0062ms
maxEvenConjunction2 sonucu: 536870910 süre 80.0046ms

Go derleyicisinin neden kodu optimize etmediğine ve birincisi yanlış olsa bile her zaman ikinci koşulu kontrol ettiğine dair net bir açıklama bulamadım. Ya da belki gözlerim sadece bulanıktır ve bariz bir hata göremiyorumdur? Yoksa derleyiciye bazı özel talimatlar vermeniz mi gerekiyor? Mantıklı yorum yaparsanız sevinirim.

Not: Evet, sırf eğlence olsun diye Java 5 ve Java 7/8'de benzer testler yaptım - her şey açık, yürütme süresi aynı.

Kaynak: habr.com

Yorum ekle