เงื่อนไขใน 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 นั้นมีค่าเฉลี่ยโดยเฉลี่ย ประมาณ 813MB - สิ่งนี้ส่งผลต่อความน่าเชื่อถือของผลลัพธ์ด้วย คุณต้องบันทึกกรณีทดสอบลงในดิสก์และทำการทดสอบทั้งหมดสำหรับแต่ละเกณฑ์โดยแยกจากกัน

และตอนนี้ เมื่อคิดถึงวิธีใช้งานทั้งหมดนี้โดยมีค่าใช้จ่ายน้อยที่สุด ฉันก็แก้ไขการตรวจสอบเงื่อนไขโดยอัตโนมัติ

		
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.0066ms
ผลลัพธ์ maxEvenDividing2: 126 ระยะเวลา 79.0045ms
ผลลัพธ์ maxEvenConjunction: 126 ระยะเวลา 114.0065ms
ผลลัพธ์ maxEvenConjunction2: 126 ระยะเวลา 83.0048ms

เกณฑ์สูงสุด: 256
ผลการหาร maxEvenDividing: 254 ระยะเวลา 111.0063ms
ผลลัพธ์ maxEvenDividing2: 254 ระยะเวลา 77.0044ms
ผลลัพธ์ maxEvenConjunction: 254 ระยะเวลา 110.0063ms
ผลลัพธ์ maxEvenConjunction2: 254 ระยะเวลา 80.0046ms

เกณฑ์สูงสุด: 512
ผลการหาร maxEvenDividing: 510 ระยะเวลา 114.0066ms
ผลลัพธ์ maxEvenDividing2: 510 ระยะเวลา 80.0045ms
ผลลัพธ์ maxEvenConjunction: 510 ระยะเวลา 110.0063ms
ผลลัพธ์ maxEvenConjunction2: 510 ระยะเวลา 80.0046ms

เกณฑ์สูงสุด: 1024
ผลการหาร maxEvenDividing: 1022 ระยะเวลา 109.0063ms
ผลลัพธ์ maxEvenDividing2: 1022 ระยะเวลา 77.0044ms
ผลลัพธ์ maxEvenConjunction: 1022 ระยะเวลา 111.0063ms
ผลลัพธ์ maxEvenConjunction2: 1022 ระยะเวลา 81.0047ms

เกณฑ์สูงสุด: 2048
ผลการหาร maxEvenDividing: 2046 ระยะเวลา 114.0065ms
ผลลัพธ์ maxEvenDividing2: 2046 ระยะเวลา 79.0045ms
ผลลัพธ์ maxEvenConjunction: 2046 ระยะเวลา 113.0065ms
ผลลัพธ์ maxEvenConjunction2: 2046 ระยะเวลา 81.0046ms

เกณฑ์สูงสุด: 4096
ผลการหาร maxEvenDividing: 4094 ระยะเวลา 114.0065ms
ผลลัพธ์ maxEvenDividing2: 4094 ระยะเวลา 80.0046ms
ผลลัพธ์ maxEvenConjunction: 4094 ระยะเวลา 111.0063ms
ผลลัพธ์ maxEvenConjunction2: 4094 ระยะเวลา 78.0045ms

เกณฑ์สูงสุด: 8192
ผลการหาร maxEvenDividing: 8190 ระยะเวลา 107.0062ms
ผลลัพธ์ maxEvenDividing2: 8190 ระยะเวลา 77.0044ms
ผลลัพธ์ maxEvenConjunction: 8190 ระยะเวลา 111.0063ms
ผลลัพธ์ maxEvenConjunction2: 8190 ระยะเวลา 77.0044ms

เกณฑ์สูงสุด: 16384
ผลการหาร maxEvenDividing: 16382 ระยะเวลา 109.0063ms
ผลลัพธ์ maxEvenDividing2: 16382 ระยะเวลา 77.0044ms
ผลลัพธ์ maxEvenConjunction: 16382 ระยะเวลา 108.0062ms
ผลลัพธ์ maxEvenConjunction2: 16382 ระยะเวลา 77.0044ms

เกณฑ์สูงสุด: 32768
ผลการหาร maxEvenDividing: 32766 ระยะเวลา 112.0064ms
ผลลัพธ์ maxEvenDividing2: 32766 ระยะเวลา 77.0044ms
ผลลัพธ์ maxEvenConjunction: 32766 ระยะเวลา 109.0062ms
ผลลัพธ์ maxEvenConjunction2: 32766 ระยะเวลา 78.0045ms

เกณฑ์สูงสุด: 65536
ผลการหาร maxEvenDividing: 65534 ระยะเวลา 109.0062ms
ผลลัพธ์ maxEvenDividing2: 65534 ระยะเวลา 75.0043ms
ผลลัพธ์ maxEvenConjunction: 65534 ระยะเวลา 109.0063ms
ผลลัพธ์ maxEvenConjunction2: 65534 ระยะเวลา 79.0045ms

เกณฑ์สูงสุด: 131072
ผลการหาร maxEvenDividing: 131070 ระยะเวลา 108.0061ms
ผลลัพธ์ maxEvenDividing2: 131070 ระยะเวลา 76.0044ms
ผลลัพธ์ maxEvenConjunction: 131070 ระยะเวลา 110.0063ms
ผลลัพธ์ maxEvenConjunction2: 131070 ระยะเวลา 80.0046ms

เกณฑ์สูงสุด: 262144
ผลการหาร maxEvenDividing: 262142 ระยะเวลา 110.0063ms
ผลลัพธ์ maxEvenDividing2: 262142 ระยะเวลา 76.0044ms
ผลลัพธ์ maxEvenConjunction: 262142 ระยะเวลา 107.0061ms
ผลลัพธ์ maxEvenConjunction2: 262142 ระยะเวลา 78.0044ms

เกณฑ์สูงสุด: 524288
ผลการหาร maxEvenDividing: 524286 ระยะเวลา 109.0062ms
ผลลัพธ์ maxEvenDividing2: 524286 ระยะเวลา 78.0045ms
ผลลัพธ์ maxEvenConjunction: 524286 ระยะเวลา 109.0062ms
ผลลัพธ์ maxEvenConjunction2: 524286 ระยะเวลา 80.0046ms

เกณฑ์สูงสุด: 1048576
ผลการหาร maxEvenDividing: 1048574 ระยะเวลา 109.0063ms
ผลลัพธ์ maxEvenDividing2: 1048574 ระยะเวลา 80.0045ms
ผลลัพธ์ maxEvenConjunction: 1048574 ระยะเวลา 114.0066ms
ผลลัพธ์ maxEvenConjunction2: 1048574 ระยะเวลา 78.0044ms

เกณฑ์สูงสุด: 2097152
ผลการหาร maxEvenDividing: 2097150 ระยะเวลา 111.0064ms
ผลลัพธ์ maxEvenDividing2: 2097150 ระยะเวลา 79.0045ms
ผลลัพธ์ maxEvenConjunction: 2097150 ระยะเวลา 112.0064ms
ผลลัพธ์ maxEvenConjunction2: 2097150 ระยะเวลา 77.0044ms

เกณฑ์สูงสุด: 4194304
ผลการหาร maxEvenDividing: 4194302 ระยะเวลา 111.0063ms
ผลลัพธ์ maxEvenDividing2: 4194302 ระยะเวลา 78.0045ms
ผลลัพธ์ maxEvenConjunction: 4194302 ระยะเวลา 111.0063ms
ผลลัพธ์ maxEvenConjunction2: 4194302 ระยะเวลา 77.0044ms

เกณฑ์สูงสุด: 8388608
ผลการหาร maxEvenDividing: 8388606 ระยะเวลา 109.0062ms
ผลลัพธ์ maxEvenDividing2: 8388606 ระยะเวลา 78.0045ms
ผลลัพธ์ maxEvenConjunction: 8388606 ระยะเวลา 114.0065ms
ผลลัพธ์ maxEvenConjunction2: 8388606 ระยะเวลา 78.0045ms

เกณฑ์สูงสุด: 16777216
ผลการหาร maxEvenDividing: 16777214 ระยะเวลา 109.0062ms
ผลลัพธ์ maxEvenDividing2: 16777214 ระยะเวลา 77.0044ms
ผลลัพธ์ maxEvenConjunction: 16777214 ระยะเวลา 109.0063ms
ผลลัพธ์ maxEvenConjunction2: 16777214 ระยะเวลา 77.0044ms

เกณฑ์สูงสุด: 33554432
ผลการหาร maxEvenDividing: 33554430 ระยะเวลา 113.0065ms
ผลลัพธ์ maxEvenDividing2: 33554430 ระยะเวลา 78.0045ms
ผลลัพธ์ maxEvenConjunction: 33554430 ระยะเวลา 110.0063ms
ผลลัพธ์ maxEvenConjunction2: 33554430 ระยะเวลา 80.0045ms

เกณฑ์สูงสุด: 67108864
ผลการหาร maxEvenDividing: 67108860 ระยะเวลา 112.0064ms
ผลลัพธ์ maxEvenDividing2: 67108860 ระยะเวลา 77.0044ms
ผลลัพธ์ maxEvenConjunction: 67108860 ระยะเวลา 112.0064ms
ผลลัพธ์ maxEvenConjunction2: 67108860 ระยะเวลา 80.0046ms

เกณฑ์สูงสุด: 134217728
ผลการหาร maxEvenDividing: 134217726 ระยะเวลา 109.0063ms
ผลลัพธ์ maxEvenDividing2: 134217726 ระยะเวลา 78.0044ms
ผลลัพธ์ maxEvenConjunction: 134217726 ระยะเวลา 114.0065ms
ผลลัพธ์ maxEvenConjunction2: 134217726 ระยะเวลา 81.0047ms

เกณฑ์สูงสุด: 268435456
ผลการหาร maxEvenDividing: 268435446 ระยะเวลา 111.0064ms
ผลลัพธ์ maxEvenDividing2: 268435446 ระยะเวลา 79.0045ms
ผลลัพธ์ maxEvenConjunction: 268435446 ระยะเวลา 114.0065ms
ผลลัพธ์ maxEvenConjunction2: 268435446 ระยะเวลา 79.0045ms

เกณฑ์สูงสุด: 536870912
ผลการหาร maxEvenDividing: 536870910 ระยะเวลา 107.0062ms
ผลลัพธ์ maxEvenDividing2: 536870910 ระยะเวลา 76.0043ms
ผลลัพธ์ maxEvenConjunction: 536870910 ระยะเวลา 109.0062ms
ผลลัพธ์ maxEvenConjunction2: 536870910 ระยะเวลา 80.0046ms

ฉันไม่พบคำอธิบายที่ชัดเจนว่าเหตุใดคอมไพเลอร์ Go จึงไม่ปรับโค้ดให้เหมาะสมและตรวจสอบเงื่อนไขที่สองเสมอ แม้ว่าเงื่อนไขแรกจะเป็นเท็จก็ตาม หรือบางทีตาของฉันพร่ามัวและฉันไม่เห็นข้อผิดพลาดที่ชัดเจน? หรือคุณจำเป็นต้องให้คำแนะนำพิเศษแก่คอมไพลเลอร์หรือไม่? ฉันยินดีที่จะแสดงความคิดเห็นที่สมเหตุสมผล

PS: ใช่ เพื่อความสนุกสนาน ฉันทำการทดสอบที่คล้ายกันบน Java 5 และ Java 7/8 - ทุกอย่างชัดเจน เวลาดำเนินการก็เหมือนกัน

ที่มา: will.com

เพิ่มความคิดเห็น