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: Иә, көңіл көтеру үшін мен Java 5 және Java 7/8 нұсқаларында ұқсас сынақтарды өткіздім - бәрі түсінікті, орындау уақыты бірдей.

Ақпарат көзі: www.habr.com

пікір қалдыру