⬆️ ⬇️

Conditions in Go and their weirdness

What do you think, are performance equivalents of these two options for checking conditions inside a cycle?



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


It all started with a “warm-up for the brain”; I had to give an example of an optimal search for an array of integers [-x .... x] of the largest even number. I wondered how much better the performance would be, if to find out an even number or not, use logical multiplication by 1.



 //       0 value & 1 == 0 //vs   value % 2 == 0 


My Go programming experience is not very big, a little more than a year and a half, I used it, though often, but purely for utilitarian purposes (well, maybe except for one project related to a high-loaded http service), so I started exactly with it. Open GoLand and write a simple test.

')

 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)+"\t\tduration %s", result.Duration) //   "" result = maxEvenConjunction("maxEvenConjunction", arr) log.Printf(result.Name+"\t result: "+fmt.Sprint(result.Value)+"\t\tduration %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 } 


We get the result, which shows that the greater the threshold, the more often there are fluctuations in terms of performance.



Compare
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



It is clear that in this case, for different thresholds we have different sets of test data, the processor load (on my i5-2540M laptop) varies in the region of 20..30%, the memory occupied by the application launched from under GoLand on average about 813MB is also affects the reliability of the result, you need to implement the preservation of test sets on the disk and run all the tests for each threshold isolated from each other.



So, thinking about how to implement all this with minimal cost, I automatically correct the condition check.



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


on



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


I run the tests again ... and stop understanding anything :)



The time spent on execution begins to differ not by percents / fractions of a percent, but by 10..15%. I quickly finish 2 more tests:



 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 } 


I launch and get this picture:
initial array capacity: 100000000



max threshold: 128

maxEvenDividing result: 126 duration 116.0066ms

maxEvenDividing2 result: 126 duration 79.0045ms

maxEvenConjunction result: 126 duration 114.0065ms

maxEvenConjunction2 result: 126 duration 83.0048ms



max threshold: 256

maxEvenDividing result: 254 duration 111.0063ms

maxEvenDividing2 result: 254 duration 77.0044ms

maxEvenConjunction result: 254 duration 110.0063ms

maxEvenConjunction2 result: 254 duration 80.0046ms



max threshold: 512

maxEvenDividing result: 510 duration 114.0066ms

maxEvenDividing2 result: 510 duration 80.0045ms

maxEvenConjunction result: 510 duration 110.0063ms

maxEvenConjunction2 result: 510 duration 80.0046ms



max threshold: 1024

maxEvenDividing result: 1022 duration 109.0063ms

maxEvenDividing2 result: 1022 duration 77.0044ms

maxEvenConjunction result: 1022 duration 111.0063ms

maxEvenConjunction2 result: 1022 duration 81.0047ms



max threshold: 2048

maxEvenDividing result: 2046 duration 114.0065ms

maxEvenDividing2 result: 2046 duration 79.0045ms

maxEvenConjunction result: 2046 duration 113.0065ms

maxEvenConjunction2 result: 2046 duration 81.0046ms



max threshold: 4096

maxEvenDividing result: 4094 duration 114.0065ms

maxEvenDividing2 result: 4094 duration 80.0046ms

maxEvenConjunction result: 4094 duration 111.0063ms

maxEvenConjunction2 result: 4094 duration 78.0045ms



max threshold: 8192

maxEvenDividing result: 8190 duration 107.0062ms

maxEvenDividing2 result: 8190 duration 77.0044ms

maxEvenConjunction result: 8190 duration 111.0063ms

maxEvenConjunction2 result: 8190 duration 77.0044ms



max threshold: 16384

maxEvenDividing result: 16382 duration 109.0063ms

maxEvenDividing2 result: 16382 duration 77.0044ms

maxEvenConjunction result: 16382 duration 108.0062ms

maxEvenConjunction2 result: 16382 duration 77.0044ms



max threshold: 32768

maxEvenDividing result: 32766 duration 112.0064ms

maxEvenDividing2 result: 32766 duration 77.0044ms

maxEvenConjunction result: 32766 duration 109.0062ms

maxEvenConjunction2 result: 32766 duration 78.0045ms



max threshold: 65536

maxEvenDividing result: 65534 duration 109.0062ms

maxEvenDividing2 result: 65534 duration 75.0043ms

maxEvenConjunction result: 65534 duration 109.0063ms

maxEvenConjunction2 result: 65534 duration 79.0045ms



max threshold: 131072

maxEvenDividing result: 131070 duration 108.0061ms

maxEvenDividing2 result: 131070 duration 76.0044ms

maxEvenConjunction result: 131070 duration 110.0063ms

maxEvenConjunction2 result: 131070 duration 80.0046ms



max threshold: 262144

maxEvenDividing result: 262142 duration 110.0063ms

maxEvenDividing2 result: 262142 duration 76.0044ms

maxEvenConjunction result: 262142 duration 107.0061ms

maxEvenConjunction2 result: 262142 duration 78.0044ms



max threshold: 524288

maxEvenDividing result: 524286 duration 109.0062ms

maxEvenDividing2 result: 524286 duration 78.0045ms

maxEvenConjunction result: 524286 duration 109.0062ms

maxEvenConjunction2 result: 524286 duration 80.0046ms



max threshold: 1048576

maxEvenDividing result: 1048574 duration 109.0063ms

maxEvenDividing2 result: 1048574 duration 80.0045ms

maxEvenConjunction result: 1048574 duration 114.0066ms

maxEvenConjunction2 result: 1048574 duration 78.0044ms



max threshold: 2097152

maxEvenDividing result: 2097150 duration 111.0064ms

maxEvenDividing2 result: 2097150 duration 79.0045ms

maxEvenConjunction result: 2097150 duration 112.0064ms

maxEvenConjunction2 result: 2097150 duration 77.0044ms



max threshold: 4194304

maxEvenDividing result: 4194302 duration 111.0063ms

maxEvenDividing2 result: 4194302 duration 78.0045ms

maxEvenConjunction result: 4194302 duration 111.0063ms

maxEvenConjunction2 result: 4194302 duration 77.0044ms



max threshold: 8388608

maxEvenDividing result: 8388606 duration 109.0062ms

maxEvenDividing2 result: 8388606 duration 78.0045ms

maxEvenConjunction result: 8388606 duration 114.0065ms

maxEvenConjunction2 result: 8388606 duration 78.0045ms



max threshold: 16777216

maxEvenDividing result: 16777214 duration 109.0062ms

maxEvenDividing2 result: 16777214 duration 77.0044ms

maxEvenConjunction result: 16777214 duration 109.0063ms

maxEvenConjunction2 result: 16777214 duration 77.0044ms



max threshold: 33554432

maxEvenDividing result: 33554430 duration 113.0065ms

maxEvenDividing2 result: 33554430 duration 78.0045ms

maxEvenConjunction result: 33554430 duration 110.0063ms

maxEvenConjunction2 result: 33554430 duration 80.0045ms



max threshold: 67108864

maxEvenDividing result: 67108860 duration 112.0064ms

maxEvenDividing2 result: 67108860 duration 77.0044ms

maxEvenConjunction result: 67108860 duration 112.0064ms

maxEvenConjunction2 result: 67108860 duration 80.0046ms



max threshold: 134217728

maxEvenDividing result: 134217726 duration 109.0063ms

maxEvenDividing2 result: 134217726 duration 78.0044ms

maxEvenConjunction result: 134217726 duration 114.0065ms

maxEvenConjunction2 result: 134217726 duration 81.0047ms



max threshold: 268435456

maxEvenDividing result: 268435446 duration 111.0064ms

maxEvenDividing2 result: 268435446 duration 79.0045ms

maxEvenConjunction result: 268435446 duration 114.0065ms

maxEvenConjunction2 result: 268435446 duration 79.0045ms



max threshold: 536870912

maxEvenDividing result: 536870910 duration 107.0062ms

maxEvenDividing2 result: 536870910 duration 76.0043ms

maxEvenConjunction result: 536870910 duration 109.0062ms

maxEvenConjunction2 result: 536870910 duration 80.0046ms



A clear explanation of why the Go compiler does not optimize the code and always checks the second condition, even if the first one is false - I did not find it. Or maybe I just had an eye "zamililsya" and I do not see some obvious error? Or do I need to specify some special instructions to the compiler? I would be happy sensible comments.



PS: Yes, for interest, drove similar tests for Java 5 and Java 7/8 - everything is clear, the execution time is the same.

Source: https://habr.com/ru/post/444182/



All Articles