性能测试之for...range循环

Golang for…range循环

  • 测试平台: M1 Macbook Pro
  • golang版本: 1.18.3

测试代码

package for_range

import "testing"

const N = 1000000

type Tmp struct {
	A string
	B string
	C int
	D bool
}

func initMap() map[int]string {
	s := make(map[int]string, N)
	for i := 0; i < N; i++ {
		s[i] = "1"
	}
	return s
}

func initSlice() []Tmp {
	s := make([]Tmp, N)
	for i := 0; i < N; i++ {
		s[i] = Tmp{
			A: "1",
			B: "2",
			C: i,
			D: true,
		}
	}
	return s
}

func initSlicePtr() []*Tmp {
	s := make([]*Tmp, N)
	for i := 0; i < N; i++ {
		s[i] = &Tmp{
			A: "1",
			B: "2",
			C: i,
			D: true,
		}
	}
	return s
}

func ForRangeKeySlice(s []Tmp) {
	for i := range s {
		a, b := i, s[i]
		_, _ = a, b
	}
}

func ForRangeSlice(s []Tmp) {
	for i, v := range s {
		a, b := i, v
		_, _ = a, b
	}
}

func ForRangeKeySlicePtr(s []*Tmp) {
	for i := range s {
		a, b := i, s[i]
		_, _ = a, b
	}
}

func ForRangeSlicePtr(s []*Tmp) {
	for i, v := range s {
		a, b := i, v
		_, _ = a, b
	}
}

func ForRangeMapWithValue(s map[int]string) {
	for i, v := range s {
		a, b := i, v
		_, _ = a, b
	}
}

func ForRangeMapWithoutValue(s map[int]string) {
	for i := range s {
		a, b := i, s[i]
		_, _ = a, b
	}
}

func BenchmarkForMapWithValue(b *testing.B) {
	s := initMap()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ForRangeMapWithValue(s)
	}
}

func BenchmarkForMapWithoutValue(b *testing.B) {
	s := initMap()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ForRangeMapWithoutValue(s)
	}
}

func BenchmarkForRangeKeySlice(b *testing.B) {
	s := initSlice()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ForRangeKeySlice(s)
	}
}

func BenchmarkForRangeSlice(b *testing.B) {
	s := initSlice()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ForRangeSlice(s)
	}
}

func BenchmarkForRangeKeySlicePtr(b *testing.B) {
	s := initSlicePtr()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ForRangeKeySlicePtr(s)
	}
}

func BenchmarkForRangeSlicePtr(b *testing.B) {
	s := initSlicePtr()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		ForRangeSlicePtr(s)
	}
}

测试结果

go test -bench=. -benchmem -run=none

goos: darwin
goarch: arm64
pkg: golang_benchmark/for_range
BenchmarkForMapWithValue-8                   135           8839299 ns/op               0 B/op          0 allocs/op
BenchmarkForMapWithoutValue-8                 85          14047786 ns/op               0 B/op          0 allocs/op
BenchmarkForRangeKeySlice-8                 3813            313135 ns/op               0 B/op          0 allocs/op
BenchmarkForRangeSlice-8                     474           2518749 ns/op               0 B/op          0 allocs/op
BenchmarkForRangeKeySlicePtr-8              3832            313097 ns/op               0 B/op          0 allocs/op
BenchmarkForRangeSlicePtr-8                 3836            313212 ns/op               0 B/op          0 allocs/op
PASS
ok      golang_benchmark/for_range      9.593s


测试结论

  • 遍历map时若要取值请直接在遍历的时候取用
  • 遍历slice时当value为指针时直接取和靠索引取性能差异不大,当时value为非指针时通过索引取值的性能是直接取的9-10倍(具体倍数与对象大小有关)