الظروف في الذهاب والمراوغات الخاصة بهم

هل تعتقد أن هذين الخيارين لظروف الاختبار داخل الحلقة متساويان في الأداء؟

		
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%. وأضيف بسرعة اختبارين آخرين:

		
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: نعم، من أجل المتعة فقط، قمت بإجراء اختبارات مماثلة على Java 5 وJava 7/8 - كل شيء واضح، ووقت التنفيذ هو نفسه.

المصدر: www.habr.com

إضافة تعليق