Go-da şərtlər və onların qəribəlikləri

Sizcə, dövrə daxilində şərtləri yoxlamaq üçün bu iki variant performans baxımından bərabərdirmi?

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


Hər şey “beyin istiləşməsi” ilə başladı; mən [-x....x] tam ədədlər massivində ən böyük cüt ədəd üçün optimal axtarış nümunəsi verməli oldum. Ədədin cüt olub olmadığını anlamaq üçün 1-ə məntiqi vurma üsulundan istifadə etsəm, performansın nə qədər yaxşı olacağını düşünürdüm.


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

Go-da proqramlaşdırma təcrübəm çox geniş deyil, cəmi bir il yarımdan çoxdur, mən ondan tez-tez olsa da, sırf utilitar məqsədlər üçün istifadə etdim (yaxşı, yüksək yüklü http xidməti ilə əlaqəli bir layihə istisna olmaqla) onunla başladı. GoLand-ı açın və sadə 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 nə qədər yüksək olsa, performansda dalğalanmaların daha tez-tez göründüyünü göstərən bir nəticə əldə edirik.

Müqayisə etmax 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

Aydındır ki, bu halda, müxtəlif həddlər üçün müxtəlif test məlumat dəstlərimiz var, prosessor yükü (mənim i5-2540M noutbukumda) təxminən 20..30% dəyişir, GoLand-dan işləyən tətbiqin tutduğu yaddaş orta hesabladır. təqribən 813MB - bu da nəticənin etibarlılığına təsir edir, siz diskdə test hadisələrini saxlamalı və bir-birindən təcrid olunmuş şəkildə hər hədd üçün bütün testləri keçirməlisiniz.

İndi bütün bunları minimal xərclərlə necə həyata keçirəcəyimi düşünərək, vəziyyətin yoxlanışını avtomatik olaraq düzəldirəm

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

haqqında

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

Testləri yenidən keçirirəm... və heç nə başa düşməyi dayandırıram :)

İcraya sərf olunan vaxt artıq faizlə/faizlə deyil, 10..15% ilə fərqlənməyə başlayır. Tezliklə daha 2 test əlavə edirəm:

		
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
}

Mən onu işlədirəm və bu şəkli alıram:ilkin massiv tutumu: 100000000

maksimum həddi: 128
maxEvenDividing nəticəsi: 126 müddət 116.0066ms
maxEvenDividing2 nəticəsi: 126 müddət 79.0045ms
maxEvenConjunction nəticəsi: 126 müddət 114.0065ms
maxEvenConjunction2 nəticəsi: 126 müddət 83.0048ms

maksimum həddi: 256
maxEvenDividing nəticəsi: 254 müddət 111.0063ms
maxEvenDividing2 nəticəsi: 254 müddət 77.0044ms
maxEvenConjunction nəticəsi: 254 müddət 110.0063ms
maxEvenConjunction2 nəticəsi: 254 müddət 80.0046ms

maksimum həddi: 512
maxEvenDividing nəticəsi: 510 müddət 114.0066ms
maxEvenDividing2 nəticəsi: 510 müddət 80.0045ms
maxEvenConjunction nəticəsi: 510 müddət 110.0063ms
maxEvenConjunction2 nəticəsi: 510 müddət 80.0046ms

maksimum həddi: 1024
maxEvenDividing nəticəsi: 1022 müddət 109.0063ms
maxEvenDividing2 nəticəsi: 1022 müddət 77.0044ms
maxEvenConjunction nəticəsi: 1022 müddət 111.0063ms
maxEvenConjunction2 nəticəsi: 1022 müddət 81.0047ms

maksimum həddi: 2048
maxEvenDividing nəticəsi: 2046 müddət 114.0065ms
maxEvenDividing2 nəticəsi: 2046 müddət 79.0045ms
maxEvenConjunction nəticəsi: 2046 müddət 113.0065ms
maxEvenConjunction2 nəticəsi: 2046 müddət 81.0046ms

maksimum həddi: 4096
maxEvenDividing nəticəsi: 4094 müddət 114.0065ms
maxEvenDividing2 nəticəsi: 4094 müddət 80.0046ms
maxEvenConjunction nəticəsi: 4094 müddət 111.0063ms
maxEvenConjunction2 nəticəsi: 4094 müddət 78.0045ms

maksimum həddi: 8192
maxEvenDividing nəticəsi: 8190 müddət 107.0062ms
maxEvenDividing2 nəticəsi: 8190 müddət 77.0044ms
maxEvenConjunction nəticəsi: 8190 müddət 111.0063ms
maxEvenConjunction2 nəticəsi: 8190 müddət 77.0044ms

maksimum həddi: 16384
maxEvenDividing nəticəsi: 16382 müddət 109.0063ms
maxEvenDividing2 nəticəsi: 16382 müddət 77.0044ms
maxEvenConjunction nəticəsi: 16382 müddət 108.0062ms
maxEvenConjunction2 nəticəsi: 16382 müddət 77.0044ms

maksimum həddi: 32768
maxEvenDividing nəticəsi: 32766 müddət 112.0064ms
maxEvenDividing2 nəticəsi: 32766 müddət 77.0044ms
maxEvenConjunction nəticəsi: 32766 müddət 109.0062ms
maxEvenConjunction2 nəticəsi: 32766 müddət 78.0045ms

maksimum həddi: 65536
maxEvenDividing nəticəsi: 65534 müddət 109.0062ms
maxEvenDividing2 nəticəsi: 65534 müddət 75.0043ms
maxEvenConjunction nəticəsi: 65534 müddət 109.0063ms
maxEvenConjunction2 nəticəsi: 65534 müddət 79.0045ms

maksimum həddi: 131072
maxEvenDividing nəticəsi: 131070 müddət 108.0061ms
maxEvenDividing2 nəticəsi: 131070 müddət 76.0044ms
maxEvenConjunction nəticəsi: 131070 müddət 110.0063ms
maxEvenConjunction2 nəticəsi: 131070 müddət 80.0046ms

maksimum həddi: 262144
maxEvenDividing nəticəsi: 262142 müddət 110.0063ms
maxEvenDividing2 nəticəsi: 262142 müddət 76.0044ms
maxEvenConjunction nəticəsi: 262142 müddət 107.0061ms
maxEvenConjunction2 nəticəsi: 262142 müddət 78.0044ms

maksimum həddi: 524288
maxEvenDividing nəticəsi: 524286 müddət 109.0062ms
maxEvenDividing2 nəticəsi: 524286 müddət 78.0045ms
maxEvenConjunction nəticəsi: 524286 müddət 109.0062ms
maxEvenConjunction2 nəticəsi: 524286 müddət 80.0046ms

maksimum həddi: 1048576
maxEvenDividing nəticəsi: 1048574 müddət 109.0063ms
maxEvenDividing2 nəticəsi: 1048574 müddət 80.0045ms
maxEvenConjunction nəticəsi: 1048574 müddət 114.0066ms
maxEvenConjunction2 nəticəsi: 1048574 müddət 78.0044ms

maksimum həddi: 2097152
maxEvenDividing nəticəsi: 2097150 müddət 111.0064ms
maxEvenDividing2 nəticəsi: 2097150 müddət 79.0045ms
maxEvenConjunction nəticəsi: 2097150 müddət 112.0064ms
maxEvenConjunction2 nəticəsi: 2097150 müddət 77.0044ms

maksimum həddi: 4194304
maxEvenDividing nəticəsi: 4194302 müddət 111.0063ms
maxEvenDividing2 nəticəsi: 4194302 müddət 78.0045ms
maxEvenConjunction nəticəsi: 4194302 müddət 111.0063ms
maxEvenConjunction2 nəticəsi: 4194302 müddət 77.0044ms

maksimum həddi: 8388608
maxEvenDividing nəticəsi: 8388606 müddət 109.0062ms
maxEvenDividing2 nəticəsi: 8388606 müddət 78.0045ms
maxEvenConjunction nəticəsi: 8388606 müddət 114.0065ms
maxEvenConjunction2 nəticəsi: 8388606 müddət 78.0045ms

maksimum həddi: 16777216
maxEvenDividing nəticəsi: 16777214 müddət 109.0062ms
maxEvenDividing2 nəticəsi: 16777214 müddət 77.0044ms
maxEvenConjunction nəticəsi: 16777214 müddət 109.0063ms
maxEvenConjunction2 nəticəsi: 16777214 müddət 77.0044ms

maksimum həddi: 33554432
maxEvenDividing nəticəsi: 33554430 müddət 113.0065ms
maxEvenDividing2 nəticəsi: 33554430 müddət 78.0045ms
maxEvenConjunction nəticəsi: 33554430 müddət 110.0063ms
maxEvenConjunction2 nəticəsi: 33554430 müddət 80.0045ms

maksimum həddi: 67108864
maxEvenDividing nəticəsi: 67108860 müddət 112.0064ms
maxEvenDividing2 nəticəsi: 67108860 müddət 77.0044ms
maxEvenConjunction nəticəsi: 67108860 müddət 112.0064ms
maxEvenConjunction2 nəticəsi: 67108860 müddət 80.0046ms

maksimum həddi: 134217728
maxEvenDividing nəticəsi: 134217726 müddət 109.0063ms
maxEvenDividing2 nəticəsi: 134217726 müddət 78.0044ms
maxEvenConjunction nəticəsi: 134217726 müddət 114.0065ms
maxEvenConjunction2 nəticəsi: 134217726 müddət 81.0047ms

maksimum həddi: 268435456
maxEvenDividing nəticəsi: 268435446 müddət 111.0064ms
maxEvenDividing2 nəticəsi: 268435446 müddət 79.0045ms
maxEvenConjunction nəticəsi: 268435446 müddət 114.0065ms
maxEvenConjunction2 nəticəsi: 268435446 müddət 79.0045ms

maksimum həddi: 536870912
maxEvenDividing nəticəsi: 536870910 müddət 107.0062ms
maxEvenDividing2 nəticəsi: 536870910 müddət 76.0043ms
maxEvenConjunction nəticəsi: 536870910 müddət 109.0062ms
maxEvenConjunction2 nəticəsi: 536870910 müddət 80.0046ms

Go kompilyatorunun niyə kodu optimallaşdırmadığını və birincisi yanlış olsa belə, həmişə ikinci şərti yoxladığını aydın izah edə bilmədim. Yoxsa gözlərim sadəcə bulanıqdır və mən heç bir aşkar səhv görmürəm? Yoxsa kompilyatora bəzi xüsusi göstərişlər vermək lazımdır? Məntiqli şərhlərə şad olardım.

PS: Bəli, sadəcə əylənmək üçün Java 5 və Java 7/8-də oxşar testlər keçirdim - hər şey aydındır, icra müddəti eynidir.

Mənbə: www.habr.com

Добавить комментарий