Source file src/cmd/compile/internal/test/switch_test.go

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package test
     6  
     7  import (
     8  	"math/bits"
     9  	"testing"
    10  )
    11  
    12  func BenchmarkSwitch8Predictable(b *testing.B) {
    13  	benchmarkSwitch8(b, true)
    14  }
    15  func BenchmarkSwitch8Unpredictable(b *testing.B) {
    16  	benchmarkSwitch8(b, false)
    17  }
    18  func benchmarkSwitch8(b *testing.B, predictable bool) {
    19  	n := 0
    20  	rng := newRNG()
    21  	for i := 0; i < b.N; i++ {
    22  		rng = rng.next(predictable)
    23  		switch rng.value() & 7 {
    24  		case 0:
    25  			n += 1
    26  		case 1:
    27  			n += 2
    28  		case 2:
    29  			n += 3
    30  		case 3:
    31  			n += 4
    32  		case 4:
    33  			n += 5
    34  		case 5:
    35  			n += 6
    36  		case 6:
    37  			n += 7
    38  		case 7:
    39  			n += 8
    40  		}
    41  	}
    42  	sink = n
    43  }
    44  
    45  func BenchmarkSwitch32Predictable(b *testing.B) {
    46  	benchmarkSwitch32(b, true)
    47  }
    48  func BenchmarkSwitch32Unpredictable(b *testing.B) {
    49  	benchmarkSwitch32(b, false)
    50  }
    51  func benchmarkSwitch32(b *testing.B, predictable bool) {
    52  	n := 0
    53  	rng := newRNG()
    54  	for i := 0; i < b.N; i++ {
    55  		rng = rng.next(predictable)
    56  		switch rng.value() & 31 {
    57  		case 0, 1, 2:
    58  			n += 1
    59  		case 4, 5, 6:
    60  			n += 2
    61  		case 8, 9, 10:
    62  			n += 3
    63  		case 12, 13, 14:
    64  			n += 4
    65  		case 16, 17, 18:
    66  			n += 5
    67  		case 20, 21, 22:
    68  			n += 6
    69  		case 24, 25, 26:
    70  			n += 7
    71  		case 28, 29, 30:
    72  			n += 8
    73  		default:
    74  			n += 9
    75  		}
    76  	}
    77  	sink = n
    78  }
    79  
    80  func BenchmarkSwitchStringPredictable(b *testing.B) {
    81  	benchmarkSwitchString(b, true)
    82  }
    83  func BenchmarkSwitchStringUnpredictable(b *testing.B) {
    84  	benchmarkSwitchString(b, false)
    85  }
    86  func benchmarkSwitchString(b *testing.B, predictable bool) {
    87  	a := []string{
    88  		"foo",
    89  		"foo1",
    90  		"foo22",
    91  		"foo333",
    92  		"foo4444",
    93  		"foo55555",
    94  		"foo666666",
    95  		"foo7777777",
    96  	}
    97  	n := 0
    98  	rng := newRNG()
    99  	for i := 0; i < b.N; i++ {
   100  		rng = rng.next(predictable)
   101  		switch a[rng.value()&7] {
   102  		case "foo":
   103  			n += 1
   104  		case "foo1":
   105  			n += 2
   106  		case "foo22":
   107  			n += 3
   108  		case "foo333":
   109  			n += 4
   110  		case "foo4444":
   111  			n += 5
   112  		case "foo55555":
   113  			n += 6
   114  		case "foo666666":
   115  			n += 7
   116  		case "foo7777777":
   117  			n += 8
   118  		}
   119  	}
   120  	sink = n
   121  }
   122  
   123  func BenchmarkSwitchTypePredictable(b *testing.B) {
   124  	benchmarkSwitchType(b, true)
   125  }
   126  func BenchmarkSwitchTypeUnpredictable(b *testing.B) {
   127  	benchmarkSwitchType(b, false)
   128  }
   129  func benchmarkSwitchType(b *testing.B, predictable bool) {
   130  	a := []any{
   131  		int8(1),
   132  		int16(2),
   133  		int32(3),
   134  		int64(4),
   135  		uint8(5),
   136  		uint16(6),
   137  		uint32(7),
   138  		uint64(8),
   139  	}
   140  	n := 0
   141  	rng := newRNG()
   142  	for i := 0; i < b.N; i++ {
   143  		rng = rng.next(predictable)
   144  		switch a[rng.value()&7].(type) {
   145  		case int8:
   146  			n += 1
   147  		case int16:
   148  			n += 2
   149  		case int32:
   150  			n += 3
   151  		case int64:
   152  			n += 4
   153  		case uint8:
   154  			n += 5
   155  		case uint16:
   156  			n += 6
   157  		case uint32:
   158  			n += 7
   159  		case uint64:
   160  			n += 8
   161  		}
   162  	}
   163  	sink = n
   164  }
   165  
   166  func BenchmarkSwitchInterfaceTypePredictable(b *testing.B) {
   167  	benchmarkSwitchInterfaceType(b, true)
   168  }
   169  func BenchmarkSwitchInterfaceTypeUnpredictable(b *testing.B) {
   170  	benchmarkSwitchInterfaceType(b, false)
   171  }
   172  
   173  type SI0 interface {
   174  	si0()
   175  }
   176  type ST0 struct {
   177  }
   178  
   179  func (ST0) si0() {
   180  }
   181  
   182  type SI1 interface {
   183  	si1()
   184  }
   185  type ST1 struct {
   186  }
   187  
   188  func (ST1) si1() {
   189  }
   190  
   191  type SI2 interface {
   192  	si2()
   193  }
   194  type ST2 struct {
   195  }
   196  
   197  func (ST2) si2() {
   198  }
   199  
   200  type SI3 interface {
   201  	si3()
   202  }
   203  type ST3 struct {
   204  }
   205  
   206  func (ST3) si3() {
   207  }
   208  
   209  type SI4 interface {
   210  	si4()
   211  }
   212  type ST4 struct {
   213  }
   214  
   215  func (ST4) si4() {
   216  }
   217  
   218  type SI5 interface {
   219  	si5()
   220  }
   221  type ST5 struct {
   222  }
   223  
   224  func (ST5) si5() {
   225  }
   226  
   227  type SI6 interface {
   228  	si6()
   229  }
   230  type ST6 struct {
   231  }
   232  
   233  func (ST6) si6() {
   234  }
   235  
   236  type SI7 interface {
   237  	si7()
   238  }
   239  type ST7 struct {
   240  }
   241  
   242  func (ST7) si7() {
   243  }
   244  
   245  func benchmarkSwitchInterfaceType(b *testing.B, predictable bool) {
   246  	a := []any{
   247  		ST0{},
   248  		ST1{},
   249  		ST2{},
   250  		ST3{},
   251  		ST4{},
   252  		ST5{},
   253  		ST6{},
   254  		ST7{},
   255  	}
   256  	n := 0
   257  	rng := newRNG()
   258  	for i := 0; i < b.N; i++ {
   259  		rng = rng.next(predictable)
   260  		switch a[rng.value()&7].(type) {
   261  		case SI0:
   262  			n += 1
   263  		case SI1:
   264  			n += 2
   265  		case SI2:
   266  			n += 3
   267  		case SI3:
   268  			n += 4
   269  		case SI4:
   270  			n += 5
   271  		case SI5:
   272  			n += 6
   273  		case SI6:
   274  			n += 7
   275  		case SI7:
   276  			n += 8
   277  		}
   278  	}
   279  	sink = n
   280  }
   281  
   282  // A simple random number generator used to make switches conditionally predictable.
   283  type rng uint64
   284  
   285  func newRNG() rng {
   286  	return 1
   287  }
   288  func (r rng) next(predictable bool) rng {
   289  	if predictable {
   290  		return r + 1
   291  	}
   292  	return rng(bits.RotateLeft64(uint64(r), 13) * 0x3c374d)
   293  }
   294  func (r rng) value() uint64 {
   295  	return uint64(r)
   296  }
   297  
   298  // Benchmarks for switch-to-lookup-table optimization.
   299  // These use functions that return constants, which is the pattern
   300  // the lookup table optimization targets.
   301  
   302  //go:noinline
   303  func switchLookup8(x int) int {
   304  	switch x {
   305  	case 0:
   306  		return 1
   307  	case 1:
   308  		return 2
   309  	case 2:
   310  		return 3
   311  	case 3:
   312  		return 5
   313  	case 4:
   314  		return 8
   315  	case 5:
   316  		return 13
   317  	case 6:
   318  		return 21
   319  	case 7:
   320  		return 34
   321  	default:
   322  		return 0
   323  	}
   324  }
   325  
   326  //go:noinline
   327  func switchLookup32(x int) int {
   328  	switch x {
   329  	case 0:
   330  		return 10
   331  	case 1:
   332  		return 20
   333  	case 2:
   334  		return 30
   335  	case 3:
   336  		return 40
   337  	case 4:
   338  		return 50
   339  	case 5:
   340  		return 60
   341  	case 6:
   342  		return 70
   343  	case 7:
   344  		return 80
   345  	case 8:
   346  		return 90
   347  	case 9:
   348  		return 100
   349  	case 10:
   350  		return 110
   351  	case 11:
   352  		return 120
   353  	case 12:
   354  		return 130
   355  	case 13:
   356  		return 140
   357  	case 14:
   358  		return 150
   359  	case 15:
   360  		return 160
   361  	case 16:
   362  		return 170
   363  	case 17:
   364  		return 180
   365  	case 18:
   366  		return 190
   367  	case 19:
   368  		return 200
   369  	case 20:
   370  		return 210
   371  	case 21:
   372  		return 220
   373  	case 22:
   374  		return 230
   375  	case 23:
   376  		return 240
   377  	case 24:
   378  		return 250
   379  	case 25:
   380  		return 260
   381  	case 26:
   382  		return 270
   383  	case 27:
   384  		return 280
   385  	case 28:
   386  		return 290
   387  	case 29:
   388  		return 300
   389  	case 30:
   390  		return 310
   391  	case 31:
   392  		return 320
   393  	default:
   394  		return 0
   395  	}
   396  }
   397  
   398  func BenchmarkSwitchLookup8Predictable(b *testing.B) {
   399  	benchmarkSwitchLookup8(b, true)
   400  }
   401  func BenchmarkSwitchLookup8Unpredictable(b *testing.B) {
   402  	benchmarkSwitchLookup8(b, false)
   403  }
   404  func benchmarkSwitchLookup8(b *testing.B, predictable bool) {
   405  	n := 0
   406  	rng := newRNG()
   407  	for i := 0; i < b.N; i++ {
   408  		rng = rng.next(predictable)
   409  		n += switchLookup8(int(rng.value() & 7))
   410  	}
   411  	sink = n
   412  }
   413  
   414  func BenchmarkSwitchLookup32Predictable(b *testing.B) {
   415  	benchmarkSwitchLookup32(b, true)
   416  }
   417  func BenchmarkSwitchLookup32Unpredictable(b *testing.B) {
   418  	benchmarkSwitchLookup32(b, false)
   419  }
   420  func benchmarkSwitchLookup32(b *testing.B, predictable bool) {
   421  	n := 0
   422  	rng := newRNG()
   423  	for i := 0; i < b.N; i++ {
   424  		rng = rng.next(predictable)
   425  		n += switchLookup32(int(rng.value() & 31))
   426  	}
   427  	sink = n
   428  }
   429  

View as plain text