Skip to content

Instantly share code, notes, and snippets.

@mwittig
Last active December 15, 2022 20:18
Show Gist options
  • Save mwittig/75aa4e1591f5fab163875cb8923065c2 to your computer and use it in GitHub Desktop.
Save mwittig/75aa4e1591f5fab163875cb8923065c2 to your computer and use it in GitHub Desktop.
Benchmark TypeSwitch vs. Reflect vs fmt.Sprintf performance compare two value for type equality
package reflectperf
import (
"fmt"
"reflect"
"testing"
)
// Note, switchTest produces a different result for nil, nil then reflectTest and sprintfTypeTest.
// However, this case is not part of the tests performed.
// Results:
// goos: darwin
// goarch: arm64
// Benchmark_TypeSwitch
// Benchmark_TypeSwitch-10 61980268 19.23 ns/op
// Benchmark_Reflect
// Benchmark_Reflect-10 35567805 33.60 ns/op
// Benchmark_Sprintf
// Benchmark_Sprintf-10 35686405 33.63 ns/op
// PASS
type InstallCommand struct{}
type ActivateCommand struct{}
type PersonalizeCommand struct{}
type ServiceCommand interface {
isServiceCommandType()
}
func (sc *InstallCommand) isServiceCommandType() {}
func (sc *ActivateCommand) isServiceCommandType() {}
func (sc *PersonalizeCommand) isServiceCommandType() {}
func switchTest(serviceCommands []ServiceCommand, serviceCommand ServiceCommand) int {
for _, cmd := range serviceCommands {
switch cmd.(type) {
case *InstallCommand:
if _, ok := serviceCommand.(*InstallCommand); ok {
return 1
}
case *ActivateCommand:
if _, ok := serviceCommand.(*ActivateCommand); ok {
return 1
}
case *PersonalizeCommand:
if _, ok := serviceCommand.(*PersonalizeCommand); ok {
return 1
}
default:
return 0 // serviceCommands contains unhandled service command or nil
}
}
return 0 // serviceCommands is empty
}
func reflectTest(serviceCommands []ServiceCommand, serviceCommand ServiceCommand) int {
for _, cmd := range serviceCommands {
if reflect.TypeOf(cmd) == reflect.TypeOf(serviceCommand) {
return 1
}
}
return 0 // serviceCommands is empty
}
func sprintfTypeTest(serviceCommands []ServiceCommand, serviceCommand ServiceCommand) int {
for _, cmd := range serviceCommands {
if fmt.Sprintf("%T", cmd) == fmt.Sprintf("%T", serviceCommand) {
return 1
}
}
return 0 // serviceCommands is empty
}
var (
serviceCommands = []ServiceCommand{&InstallCommand{}, &ActivateCommand{}, &PersonalizeCommand{}}
)
func makeArgList() []ServiceCommand {
return []ServiceCommand{&InstallCommand{}, &ActivateCommand{}, &PersonalizeCommand{}, nil}
}
func Benchmark_TypeSwitch(b *testing.B) {
sum := 0
list := makeArgList()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, val := range list {
sum = sum + switchTest(serviceCommands, val)
}
}
}
func Benchmark_Reflect(b *testing.B) {
sum := 0
list := makeArgList()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, val := range list {
sum = sum + reflectTest(serviceCommands, val)
}
}
}
func Benchmark_Sprintf(b *testing.B) {
sum := 0
list := makeArgList()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, val := range list {
sum = sum + reflectTest(serviceCommands, val)
}
}
}
func Test_Benchmark(t *testing.T) {
list := makeArgList()
sumReflect := 0
sumSwitch := 0
sumSprintf := 0
for _, val := range list {
sumReflect = sumReflect + reflectTest(serviceCommands, val)
}
for _, val := range list {
sumSwitch = sumSwitch + switchTest(serviceCommands, val)
}
for _, val := range list {
sumSprintf = sumSprintf + sprintfTypeTest(serviceCommands, val)
}
if sumReflect != sumSwitch || sumReflect != sumSprintf {
t.Errorf("summed test values don't match sumSwitch=%d sumReflect=%d sumSprintf=%d", sumSwitch, sumReflect, sumSprintf)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment