Golang foreach的区别

func Benchmark_foreach1(b *testing.B) {
        var test map[int]int
        test = make(map[int]int)
            for i := 0; i < 100000; i++ {
                        test[i] = 1
            }
            for i := 0; i < b.N; i++ {
                    for i, _ := range test {
                            if test[i] != 1 {
                                    panic("ds")
                            }
                    }
            }
}

func Benchmark_foreach2(b *testing.B) {
            var test map[int]int
            test = make(map[int]int)
            for i := 0; i < 100000; i++ {
                    test[i] = 1
            }
            for i := 0; i < b.N; i++ {
                    for _, v := range test {
                            if v != 1 {
                                    panic("heh")
                            }
                    }
            }
}

run with result as below

goos: linux
goarch: amd64
Benchmark_foreach1-2         500           3172323 ns/op
Benchmark_foreach2-2        1000           1707214 ns/op

why is foreach-2 slow?

I think Benchmark_foreach2-2 is about 2 times faster - it requires 1707214 nanoseconds per operation, and first one takes 3172323. So second one is 3172323 / 1707214 = 1.85 times faster.

Reason: second doesn't need to take value from a memory again, it already used value in v variable.

The test[k] statement in BenchmarkForeachK takes time to randomly read the value, so BenchmarkForeachK takes more time than BenchmarkForeachV, 9362945 ns/op versus 4213940 ns/op .

For example,

package main

import "testing"

func testMap() map[int]int {
    test := make(map[int]int)
    for i := 0; i < 100000; i++ {
        test[i] = 1
    }
    return test
}

func BenchmarkForeachK(b *testing.B) {
    test := testMap()
    b.ReportAllocs()
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        for k := range test {
            if test[k] != 1 {
                panic("eh")
            }
        }
    }
}

func BenchmarkForeachV(b *testing.B) {
    test := testMap()
    b.ReportAllocs()
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        for _, v := range test {
            if v != 1 {
                panic("heh")
            }
        }
    }
}

Output:

$ go test foreach_test.go -bench=.
BenchmarkForeachK-4    200    9362945 ns/op    0 B/op    0 allocs/op
BenchmarkForeachV-4    300    4213940 ns/op    0 B/op    0 allocs/op