شرایط در Go و ویژگی های آن

به نظر شما این دو گزینه برای تست شرایط داخل یک حلقه از نظر عملکرد معادل هستند؟

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


همه چیز با "گرم کردن مغز" شروع شد؛ من باید مثالی از جستجوی بهینه برای بزرگترین عدد زوج در آرایه ای از اعداد صحیح [-x...x] ارائه می کردم. می خواستم بدانم اگر از ضرب منطقی در 1 استفاده کنم تا بفهمم یک عدد زوج است یا خیر، چقدر عملکرد بهتری خواهد داشت.


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

تجربه برنامه نویسی من در Go خیلی گسترده نیست، فقط بیش از یک سال و نیم، از آن استفاده کردم، اگرچه اغلب، اما صرفاً برای اهداف سودمند (خوب، شاید به جز یک پروژه مربوط به یک سرویس http با بار بالا)، بنابراین من با آن شروع کرد. GoLand را باز کنید و یک تست ساده بنویسید


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
}

نتیجه ای دریافت می کنیم که نشان می دهد هرچه آستانه بالاتر باشد، نوسانات عملکرد بیشتر ظاهر می شود.

مقایسه کنیدmax 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

واضح است که در این مورد، برای آستانه‌های مختلف، مجموعه‌های متفاوتی از داده‌های آزمایشی داریم، بار پردازنده (در لپ‌تاپ i5-2540M من) حدود 20..30 درصد تغییر می‌کند، حافظه اشغال شده توسط برنامه در حال اجرا از GoLand به طور متوسط ​​​​است. حدود 813 مگابایت - این نیز بر قابلیت اطمینان نتیجه تأثیر می گذارد، شما باید موارد تست را روی دیسک ذخیره کنید و همه آزمایش ها را برای هر آستانه جدا از یکدیگر اجرا کنید.

و اکنون، با فکر کردن به این که چگونه همه اینها را با حداقل هزینه اجرا کنم، به طور خودکار بررسی وضعیت را اصلاح می کنم

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

بر

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

دوباره تست ها را اجرا می کنم و دیگر چیزی را نمی فهمم :)

زمان صرف شده برای اجرا دیگر نه با درصد/کسری از درصد، بلکه با 10..15 درصد متفاوت است. من به سرعت 2 تست دیگر اضافه می کنم:

		
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
}

اجراش میکنم و این عکس رو میگیرم:ظرفیت آرایه اولیه: 100000000

حداکثر آستانه: 128
نتیجه maxEvenDividing: 126 مدت زمان 116.0066 میلی‌ثانیه
نتیجه maxEvenDividing2: 126 مدت زمان 79.0045 میلی‌ثانیه
نتیجه maxEvenConjunction: 126 مدت زمان 114.0065 میلی‌ثانیه
نتیجه maxEvenConjunction2: 126 مدت زمان 83.0048 میلی‌ثانیه

حداکثر آستانه: 256
نتیجه maxEvenDividing: 254 مدت زمان 111.0063 میلی‌ثانیه
نتیجه maxEvenDividing2: 254 مدت زمان 77.0044 میلی‌ثانیه
نتیجه maxEvenConjunction: 254 مدت زمان 110.0063 میلی‌ثانیه
نتیجه maxEvenConjunction2: 254 مدت زمان 80.0046 میلی‌ثانیه

حداکثر آستانه: 512
نتیجه maxEvenDividing: 510 مدت زمان 114.0066 میلی‌ثانیه
نتیجه maxEvenDividing2: 510 مدت زمان 80.0045 میلی‌ثانیه
نتیجه maxEvenConjunction: 510 مدت زمان 110.0063 میلی‌ثانیه
نتیجه maxEvenConjunction2: 510 مدت زمان 80.0046 میلی‌ثانیه

حداکثر آستانه: 1024
نتیجه maxEvenDividing: 1022 مدت زمان 109.0063 میلی‌ثانیه
نتیجه maxEvenDividing2: 1022 مدت زمان 77.0044 میلی‌ثانیه
نتیجه maxEvenConjunction: 1022 مدت زمان 111.0063 میلی‌ثانیه
نتیجه maxEvenConjunction2: 1022 مدت زمان 81.0047 میلی‌ثانیه

حداکثر آستانه: 2048
نتیجه maxEvenDividing: 2046 مدت زمان 114.0065 میلی‌ثانیه
نتیجه maxEvenDividing2: 2046 مدت زمان 79.0045 میلی‌ثانیه
نتیجه maxEvenConjunction: 2046 مدت زمان 113.0065 میلی‌ثانیه
نتیجه maxEvenConjunction2: 2046 مدت زمان 81.0046 میلی‌ثانیه

حداکثر آستانه: 4096
نتیجه maxEvenDividing: 4094 مدت زمان 114.0065 میلی‌ثانیه
نتیجه maxEvenDividing2: 4094 مدت زمان 80.0046 میلی‌ثانیه
نتیجه maxEvenConjunction: 4094 مدت زمان 111.0063 میلی‌ثانیه
نتیجه maxEvenConjunction2: 4094 مدت زمان 78.0045 میلی‌ثانیه

حداکثر آستانه: 8192
نتیجه maxEvenDividing: 8190 مدت زمان 107.0062 میلی‌ثانیه
نتیجه maxEvenDividing2: 8190 مدت زمان 77.0044 میلی‌ثانیه
نتیجه maxEvenConjunction: 8190 مدت زمان 111.0063 میلی‌ثانیه
نتیجه maxEvenConjunction2: 8190 مدت زمان 77.0044 میلی‌ثانیه

حداکثر آستانه: 16384
نتیجه maxEvenDividing: 16382 مدت زمان 109.0063 میلی‌ثانیه
نتیجه maxEvenDividing2: 16382 مدت زمان 77.0044 میلی‌ثانیه
نتیجه maxEvenConjunction: 16382 مدت زمان 108.0062 میلی‌ثانیه
نتیجه maxEvenConjunction2: 16382 مدت زمان 77.0044 میلی‌ثانیه

حداکثر آستانه: 32768
نتیجه maxEvenDividing: 32766 مدت زمان 112.0064 میلی‌ثانیه
نتیجه maxEvenDividing2: 32766 مدت زمان 77.0044 میلی‌ثانیه
نتیجه maxEvenConjunction: 32766 مدت زمان 109.0062 میلی‌ثانیه
نتیجه maxEvenConjunction2: 32766 مدت زمان 78.0045 میلی‌ثانیه

حداکثر آستانه: 65536
نتیجه maxEvenDividing: 65534 مدت زمان 109.0062 میلی‌ثانیه
نتیجه maxEvenDividing2: 65534 مدت زمان 75.0043 میلی‌ثانیه
نتیجه maxEvenConjunction: 65534 مدت زمان 109.0063 میلی‌ثانیه
نتیجه maxEvenConjunction2: 65534 مدت زمان 79.0045 میلی‌ثانیه

حداکثر آستانه: 131072
نتیجه maxEvenDividing: 131070 مدت زمان 108.0061 میلی‌ثانیه
نتیجه maxEvenDividing2: 131070 مدت زمان 76.0044 میلی‌ثانیه
نتیجه maxEvenConjunction: 131070 مدت زمان 110.0063 میلی‌ثانیه
نتیجه maxEvenConjunction2: 131070 مدت زمان 80.0046 میلی‌ثانیه

حداکثر آستانه: 262144
نتیجه maxEvenDividing: 262142 مدت زمان 110.0063 میلی‌ثانیه
نتیجه maxEvenDividing2: 262142 مدت زمان 76.0044 میلی‌ثانیه
نتیجه maxEvenConjunction: 262142 مدت زمان 107.0061 میلی‌ثانیه
نتیجه maxEvenConjunction2: 262142 مدت زمان 78.0044 میلی‌ثانیه

حداکثر آستانه: 524288
نتیجه maxEvenDividing: 524286 مدت زمان 109.0062 میلی‌ثانیه
نتیجه maxEvenDividing2: 524286 مدت زمان 78.0045 میلی‌ثانیه
نتیجه maxEvenConjunction: 524286 مدت زمان 109.0062 میلی‌ثانیه
نتیجه maxEvenConjunction2: 524286 مدت زمان 80.0046 میلی‌ثانیه

حداکثر آستانه: 1048576
نتیجه maxEvenDividing: 1048574 مدت زمان 109.0063 میلی‌ثانیه
نتیجه maxEvenDividing2: 1048574 مدت زمان 80.0045 میلی‌ثانیه
نتیجه maxEvenConjunction: 1048574 مدت زمان 114.0066 میلی‌ثانیه
نتیجه maxEvenConjunction2: 1048574 مدت زمان 78.0044 میلی‌ثانیه

حداکثر آستانه: 2097152
نتیجه maxEvenDividing: 2097150 مدت زمان 111.0064 میلی‌ثانیه
نتیجه maxEvenDividing2: 2097150 مدت زمان 79.0045 میلی‌ثانیه
نتیجه maxEvenConjunction: 2097150 مدت زمان 112.0064 میلی‌ثانیه
نتیجه maxEvenConjunction2: 2097150 مدت زمان 77.0044 میلی‌ثانیه

حداکثر آستانه: 4194304
نتیجه maxEvenDividing: 4194302 مدت زمان 111.0063 میلی‌ثانیه
نتیجه maxEvenDividing2: 4194302 مدت زمان 78.0045 میلی‌ثانیه
نتیجه maxEvenConjunction: 4194302 مدت زمان 111.0063 میلی‌ثانیه
نتیجه maxEvenConjunction2: 4194302 مدت زمان 77.0044 میلی‌ثانیه

حداکثر آستانه: 8388608
نتیجه maxEvenDividing: 8388606 مدت زمان 109.0062 میلی‌ثانیه
نتیجه maxEvenDividing2: 8388606 مدت زمان 78.0045 میلی‌ثانیه
نتیجه maxEvenConjunction: 8388606 مدت زمان 114.0065 میلی‌ثانیه
نتیجه maxEvenConjunction2: 8388606 مدت زمان 78.0045 میلی‌ثانیه

حداکثر آستانه: 16777216
نتیجه maxEvenDividing: 16777214 مدت زمان 109.0062 میلی‌ثانیه
نتیجه maxEvenDividing2: 16777214 مدت زمان 77.0044 میلی‌ثانیه
نتیجه maxEvenConjunction: 16777214 مدت زمان 109.0063 میلی‌ثانیه
نتیجه maxEvenConjunction2: 16777214 مدت زمان 77.0044 میلی‌ثانیه

حداکثر آستانه: 33554432
نتیجه maxEvenDividing: 33554430 مدت زمان 113.0065 میلی‌ثانیه
نتیجه maxEvenDividing2: 33554430 مدت زمان 78.0045 میلی‌ثانیه
نتیجه maxEvenConjunction: 33554430 مدت زمان 110.0063 میلی‌ثانیه
نتیجه maxEvenConjunction2: 33554430 مدت زمان 80.0045 میلی‌ثانیه

حداکثر آستانه: 67108864
نتیجه maxEvenDividing: 67108860 مدت زمان 112.0064 میلی‌ثانیه
نتیجه maxEvenDividing2: 67108860 مدت زمان 77.0044 میلی‌ثانیه
نتیجه maxEvenConjunction: 67108860 مدت زمان 112.0064 میلی‌ثانیه
نتیجه maxEvenConjunction2: 67108860 مدت زمان 80.0046 میلی‌ثانیه

حداکثر آستانه: 134217728
نتیجه maxEvenDividing: 134217726 مدت زمان 109.0063 میلی‌ثانیه
نتیجه maxEvenDividing2: 134217726 مدت زمان 78.0044 میلی‌ثانیه
نتیجه maxEvenConjunction: 134217726 مدت زمان 114.0065 میلی‌ثانیه
نتیجه maxEvenConjunction2: 134217726 مدت زمان 81.0047 میلی‌ثانیه

حداکثر آستانه: 268435456
نتیجه maxEvenDividing: 268435446 مدت زمان 111.0064 میلی‌ثانیه
نتیجه maxEvenDividing2: 268435446 مدت زمان 79.0045 میلی‌ثانیه
نتیجه maxEvenConjunction: 268435446 مدت زمان 114.0065 میلی‌ثانیه
نتیجه maxEvenConjunction2: 268435446 مدت زمان 79.0045 میلی‌ثانیه

حداکثر آستانه: 536870912
نتیجه maxEvenDividing: 536870910 مدت زمان 107.0062 میلی‌ثانیه
نتیجه maxEvenDividing2: 536870910 مدت زمان 76.0043 میلی‌ثانیه
نتیجه maxEvenConjunction: 536870910 مدت زمان 109.0062 میلی‌ثانیه
نتیجه maxEvenConjunction2: 536870910 مدت زمان 80.0046 میلی‌ثانیه

من نتوانستم توضیح واضحی پیدا کنم که چرا کامپایلر Go کد را بهینه نمی کند و همیشه شرط دوم را بررسی می کند، حتی اگر شرط اول نادرست باشد. یا شاید چشمان من فقط تار هستند و هیچ اشتباه آشکاری نمی بینم؟ یا باید دستورات خاصی را به کامپایلر ارائه دهید؟ خوشحال میشم نظرات منطقی داشته باشم

PS: بله، فقط برای سرگرمی، آزمایش های مشابهی را روی جاوا 5 و جاوا 7/8 اجرا کردم - همه چیز واضح است، زمان اجرا یکسان است.

منبع: www.habr.com

اضافه کردن نظر