| // Copyright 2021 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package test |
| |
| import ( |
| "math/bits" |
| "testing" |
| ) |
| |
| func BenchmarkSwitch8Predictable(b *testing.B) { |
| benchmarkSwitch8(b, true) |
| } |
| func BenchmarkSwitch8Unpredictable(b *testing.B) { |
| benchmarkSwitch8(b, false) |
| } |
| func benchmarkSwitch8(b *testing.B, predictable bool) { |
| n := 0 |
| rng := newRNG() |
| for i := 0; i < b.N; i++ { |
| rng = rng.next(predictable) |
| switch rng.value() & 7 { |
| case 0: |
| n += 1 |
| case 1: |
| n += 2 |
| case 2: |
| n += 3 |
| case 3: |
| n += 4 |
| case 4: |
| n += 5 |
| case 5: |
| n += 6 |
| case 6: |
| n += 7 |
| case 7: |
| n += 8 |
| } |
| } |
| sink = n |
| } |
| |
| func BenchmarkSwitch32Predictable(b *testing.B) { |
| benchmarkSwitch32(b, true) |
| } |
| func BenchmarkSwitch32Unpredictable(b *testing.B) { |
| benchmarkSwitch32(b, false) |
| } |
| func benchmarkSwitch32(b *testing.B, predictable bool) { |
| n := 0 |
| rng := newRNG() |
| for i := 0; i < b.N; i++ { |
| rng = rng.next(predictable) |
| switch rng.value() & 31 { |
| case 0, 1, 2: |
| n += 1 |
| case 4, 5, 6: |
| n += 2 |
| case 8, 9, 10: |
| n += 3 |
| case 12, 13, 14: |
| n += 4 |
| case 16, 17, 18: |
| n += 5 |
| case 20, 21, 22: |
| n += 6 |
| case 24, 25, 26: |
| n += 7 |
| case 28, 29, 30: |
| n += 8 |
| default: |
| n += 9 |
| } |
| } |
| sink = n |
| } |
| |
| func BenchmarkSwitchStringPredictable(b *testing.B) { |
| benchmarkSwitchString(b, true) |
| } |
| func BenchmarkSwitchStringUnpredictable(b *testing.B) { |
| benchmarkSwitchString(b, false) |
| } |
| func benchmarkSwitchString(b *testing.B, predictable bool) { |
| a := []string{ |
| "foo", |
| "foo1", |
| "foo22", |
| "foo333", |
| "foo4444", |
| "foo55555", |
| "foo666666", |
| "foo7777777", |
| } |
| n := 0 |
| rng := newRNG() |
| for i := 0; i < b.N; i++ { |
| rng = rng.next(predictable) |
| switch a[rng.value()&7] { |
| case "foo": |
| n += 1 |
| case "foo1": |
| n += 2 |
| case "foo22": |
| n += 3 |
| case "foo333": |
| n += 4 |
| case "foo4444": |
| n += 5 |
| case "foo55555": |
| n += 6 |
| case "foo666666": |
| n += 7 |
| case "foo7777777": |
| n += 8 |
| } |
| } |
| sink = n |
| } |
| |
| func BenchmarkSwitchTypePredictable(b *testing.B) { |
| benchmarkSwitchType(b, true) |
| } |
| func BenchmarkSwitchTypeUnpredictable(b *testing.B) { |
| benchmarkSwitchType(b, false) |
| } |
| func benchmarkSwitchType(b *testing.B, predictable bool) { |
| a := []any{ |
| int8(1), |
| int16(2), |
| int32(3), |
| int64(4), |
| uint8(5), |
| uint16(6), |
| uint32(7), |
| uint64(8), |
| } |
| n := 0 |
| rng := newRNG() |
| for i := 0; i < b.N; i++ { |
| rng = rng.next(predictable) |
| switch a[rng.value()&7].(type) { |
| case int8: |
| n += 1 |
| case int16: |
| n += 2 |
| case int32: |
| n += 3 |
| case int64: |
| n += 4 |
| case uint8: |
| n += 5 |
| case uint16: |
| n += 6 |
| case uint32: |
| n += 7 |
| case uint64: |
| n += 8 |
| } |
| } |
| sink = n |
| } |
| |
| func BenchmarkSwitchInterfaceTypePredictable(b *testing.B) { |
| benchmarkSwitchInterfaceType(b, true) |
| } |
| func BenchmarkSwitchInterfaceTypeUnpredictable(b *testing.B) { |
| benchmarkSwitchInterfaceType(b, false) |
| } |
| |
| type SI0 interface { |
| si0() |
| } |
| type ST0 struct { |
| } |
| |
| func (ST0) si0() { |
| } |
| |
| type SI1 interface { |
| si1() |
| } |
| type ST1 struct { |
| } |
| |
| func (ST1) si1() { |
| } |
| |
| type SI2 interface { |
| si2() |
| } |
| type ST2 struct { |
| } |
| |
| func (ST2) si2() { |
| } |
| |
| type SI3 interface { |
| si3() |
| } |
| type ST3 struct { |
| } |
| |
| func (ST3) si3() { |
| } |
| |
| type SI4 interface { |
| si4() |
| } |
| type ST4 struct { |
| } |
| |
| func (ST4) si4() { |
| } |
| |
| type SI5 interface { |
| si5() |
| } |
| type ST5 struct { |
| } |
| |
| func (ST5) si5() { |
| } |
| |
| type SI6 interface { |
| si6() |
| } |
| type ST6 struct { |
| } |
| |
| func (ST6) si6() { |
| } |
| |
| type SI7 interface { |
| si7() |
| } |
| type ST7 struct { |
| } |
| |
| func (ST7) si7() { |
| } |
| |
| func benchmarkSwitchInterfaceType(b *testing.B, predictable bool) { |
| a := []any{ |
| ST0{}, |
| ST1{}, |
| ST2{}, |
| ST3{}, |
| ST4{}, |
| ST5{}, |
| ST6{}, |
| ST7{}, |
| } |
| n := 0 |
| rng := newRNG() |
| for i := 0; i < b.N; i++ { |
| rng = rng.next(predictable) |
| switch a[rng.value()&7].(type) { |
| case SI0: |
| n += 1 |
| case SI1: |
| n += 2 |
| case SI2: |
| n += 3 |
| case SI3: |
| n += 4 |
| case SI4: |
| n += 5 |
| case SI5: |
| n += 6 |
| case SI6: |
| n += 7 |
| case SI7: |
| n += 8 |
| } |
| } |
| sink = n |
| } |
| |
| // A simple random number generator used to make switches conditionally predictable. |
| type rng uint64 |
| |
| func newRNG() rng { |
| return 1 |
| } |
| func (r rng) next(predictable bool) rng { |
| if predictable { |
| return r + 1 |
| } |
| return rng(bits.RotateLeft64(uint64(r), 13) * 0x3c374d) |
| } |
| func (r rng) value() uint64 { |
| return uint64(r) |
| } |
| |
| // Benchmarks for switch-to-lookup-table optimization. |
| // These use functions that return constants, which is the pattern |
| // the lookup table optimization targets. |
| |
| //go:noinline |
| func switchLookup8(x int) int { |
| switch x { |
| case 0: |
| return 1 |
| case 1: |
| return 2 |
| case 2: |
| return 3 |
| case 3: |
| return 5 |
| case 4: |
| return 8 |
| case 5: |
| return 13 |
| case 6: |
| return 21 |
| case 7: |
| return 34 |
| default: |
| return 0 |
| } |
| } |
| |
| //go:noinline |
| func switchLookup32(x int) int { |
| switch x { |
| case 0: |
| return 10 |
| case 1: |
| return 20 |
| case 2: |
| return 30 |
| case 3: |
| return 40 |
| case 4: |
| return 50 |
| case 5: |
| return 60 |
| case 6: |
| return 70 |
| case 7: |
| return 80 |
| case 8: |
| return 90 |
| case 9: |
| return 100 |
| case 10: |
| return 110 |
| case 11: |
| return 120 |
| case 12: |
| return 130 |
| case 13: |
| return 140 |
| case 14: |
| return 150 |
| case 15: |
| return 160 |
| case 16: |
| return 170 |
| case 17: |
| return 180 |
| case 18: |
| return 190 |
| case 19: |
| return 200 |
| case 20: |
| return 210 |
| case 21: |
| return 220 |
| case 22: |
| return 230 |
| case 23: |
| return 240 |
| case 24: |
| return 250 |
| case 25: |
| return 260 |
| case 26: |
| return 270 |
| case 27: |
| return 280 |
| case 28: |
| return 290 |
| case 29: |
| return 300 |
| case 30: |
| return 310 |
| case 31: |
| return 320 |
| default: |
| return 0 |
| } |
| } |
| |
| func BenchmarkSwitchLookup8Predictable(b *testing.B) { |
| benchmarkSwitchLookup8(b, true) |
| } |
| func BenchmarkSwitchLookup8Unpredictable(b *testing.B) { |
| benchmarkSwitchLookup8(b, false) |
| } |
| func benchmarkSwitchLookup8(b *testing.B, predictable bool) { |
| n := 0 |
| rng := newRNG() |
| for i := 0; i < b.N; i++ { |
| rng = rng.next(predictable) |
| n += switchLookup8(int(rng.value() & 7)) |
| } |
| sink = n |
| } |
| |
| func BenchmarkSwitchLookup32Predictable(b *testing.B) { |
| benchmarkSwitchLookup32(b, true) |
| } |
| func BenchmarkSwitchLookup32Unpredictable(b *testing.B) { |
| benchmarkSwitchLookup32(b, false) |
| } |
| func benchmarkSwitchLookup32(b *testing.B, predictable bool) { |
| n := 0 |
| rng := newRNG() |
| for i := 0; i < b.N; i++ { |
| rng = rng.next(predictable) |
| n += switchLookup32(int(rng.value() & 31)) |
| } |
| sink = n |
| } |