Sum type benchmarks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package pet | |
import ( | |
"math/rand" | |
"testing" | |
"unsafe" | |
) | |
func BenchmarkWrapperStructSingle(b *testing.B) { | |
pet := RandomPetWrapper() | |
var j int32 = 0 | |
for i := 0; i < b.N * 10; i++ { | |
// printPetWrapper(pet) | |
j += addPetWrapper(pet) | |
} | |
} | |
func BenchmarkUnsafeSumtypeSingle(b *testing.B) { | |
pet := RandomPet() | |
var j int32 = 0 | |
for i := 0; i < b.N * 10; i++ { | |
// printPet(pet) | |
j += addPet(pet) | |
} | |
} | |
func BenchmarkInterfaceSwitchSingle(b *testing.B) { | |
pet := RandomPetInterface() | |
var j int32 = 0 | |
for i := 0; i < b.N * 10; i++ { | |
// printPetInterface(pet) | |
j += addPetInterface(pet) | |
} | |
} | |
func BenchmarkInterfaceDynamicDispatch(b *testing.B) { | |
pet := RandomPetInterface() | |
var j int32 = 0 | |
for i := 0; i < b.N * 10; i++ { | |
// printPetInterface(pet) | |
j += addPetInterfaceDynamicDispatch(pet) | |
} | |
} | |
func BenchmarkWrapperStructArray(b *testing.B) { | |
pets := make([]PetWrapper, b.N * 10) | |
for i := 0; i < b.N * 10; i++ { | |
pets[i] = RandomPetWrapper() | |
} | |
var j int32 = 0 | |
for i := 0; i < b.N * 10; i++ { | |
// printPetWrapper(pets[i]) | |
j += addPetWrapper(pets[i]) | |
} | |
} | |
func BenchmarkUnsafeSumtypeArray(b *testing.B) { | |
pets := make([]Pet, b.N * 10) | |
for i := 0; i < b.N * 10; i++ { | |
pets[i] = RandomPet() | |
} | |
var j int32 = 0 | |
for i := 0; i < b.N * 10; i++ { | |
// printPet(pets[i]) | |
j += addPet(pets[i]) | |
} | |
} | |
func BenchmarkInterfaceSwitchArray(b *testing.B) { | |
pets := make([]PetInterface, b.N * 10) | |
for i := 0; i < b.N * 10; i++ { | |
pets[i] = RandomPetInterface() | |
} | |
var j int32 = 0 | |
for i := 0; i < b.N * 10; i++ { | |
// printPetInterface(pets[i]) | |
j += addPetInterface(pets[i]) | |
} | |
} | |
func BenchmarkInterfaceArrayDynamicDispatch(b *testing.B) { | |
pets := make([]PetInterface, b.N * 10) | |
for i := 0; i < b.N * 10; i++ { | |
pets[i] = RandomPetInterface() | |
} | |
var j int32 = 0 | |
for i := 0; i < b.N * 10; i++ { | |
// printPetInterface(pets[i]) | |
j += addPetInterfaceDynamicDispatch(pets[i]) | |
} | |
} | |
type Cat struct { | |
meowVolume int32 | |
} | |
type Dog struct { | |
barkVolume int32 | |
biteStrength int32 | |
} | |
type Frog struct { | |
leapHeight int32 | |
} | |
type Llama struct { | |
spitStrength int32 | |
} | |
type Pet struct { | |
tag int32 // in32 is 4 bytes | |
data [8]uint8 // 8 bytes to fit the maximum variant (Dog) | |
} | |
type PetWrapper struct { | |
cat *Cat | |
dog *Dog | |
frog *Frog | |
llama *Llama | |
} | |
func addPetWrapper(pet PetWrapper) int32 { | |
if pet.cat != nil { | |
return pet.cat.meowVolume | |
} else if pet.dog != nil { | |
return pet.dog.barkVolume + pet.dog.biteStrength | |
} else if pet.frog != nil { | |
return pet.frog.leapHeight | |
} else if pet.llama != nil { | |
return pet.llama.spitStrength | |
} | |
return 0 | |
} | |
func addPet(pet Pet) int32 { | |
// switch pet.tag { | |
// case 0: | |
// cat := *(*Cat)(unsafe.Pointer(&pet.data)) | |
// return cat.meowVolume | |
// case 1: | |
// dog := *(*Dog)(unsafe.Pointer(&pet.data)) | |
// return dog.barkVolume + dog.biteStrength | |
// case 2: | |
// frog := *(*Frog)(unsafe.Pointer(&pet.data)) | |
// return frog.leapHeight | |
// case 3: | |
// llama := *(*Llama)(unsafe.Pointer(&pet.data)) | |
// return llama.spitStrength | |
// } | |
switch pet.tag { | |
case 0: | |
cat := *(*Cat)(unsafe.Pointer(&pet.data)) | |
return cat.meowVolume | |
case 1: | |
dog := *(*Dog)(unsafe.Pointer(&pet.data)) | |
return dog.barkVolume + dog.biteStrength | |
case 2: | |
frog := *(*Frog)(unsafe.Pointer(&pet.data)) | |
return frog.leapHeight | |
case 3: | |
llama := *(*Llama)(unsafe.Pointer(&pet.data)) | |
return llama.spitStrength | |
} | |
return 0 | |
} | |
func addPetInterfaceDynamicDispatch(pet PetInterface) int32 { | |
return pet.add() | |
} | |
func addPetInterface(pet PetInterface) int32 { | |
switch value := pet.(type) { | |
case Cat: | |
return value.meowVolume | |
case Dog: | |
return value.barkVolume + value.biteStrength | |
case Frog: | |
return value.leapHeight | |
case Llama: | |
return value.spitStrength | |
} | |
return 0 | |
} | |
// func printPetWrapper(pet PetWrapper) { | |
// if pet.cat != nil { | |
// fmt.Sprintf("Cat: %d\n", pet.cat.meowVolume) | |
// } else if pet.dog != nil { | |
// fmt.Sprintf("Dog: %d %d\n", pet.dog.barkVolume, pet.dog.biteStrength) | |
// } else if pet.frog != nil { | |
// fmt.Sprintf("Frog: %d\n", pet.frog.leapHeight) | |
// } else if pet.llama != nil { | |
// fmt.Sprintf("Llama: %d\n", pet.llama.spitStrength) | |
// } | |
// } | |
// func printPet(pet Pet) { | |
// switch pet.tag { | |
// case 0: | |
// cat := *(*Cat)(unsafe.Pointer(&pet.data)) | |
// fmt.Sprintf("Cat: %d\n", cat.meowVolume) | |
// case 1: | |
// dog := *(*Dog)(unsafe.Pointer(&pet.data)) | |
// fmt.Sprintf("Dog: %d %d\n", dog.barkVolume, dog.biteStrength) | |
// case 2: | |
// frog := *(*Frog)(unsafe.Pointer(&pet.data)) | |
// fmt.Sprintf("Frog: %d\n", frog.leapHeight) | |
// case 3: | |
// llama := *(*Llama)(unsafe.Pointer(&pet.data)) | |
// fmt.Sprintf("Llama: %d\n", llama.spitStrength) | |
// } | |
// } | |
// func printPetInterface(pet PetInterface) { | |
// switch myValue := pet.(type) { | |
// case Cat: | |
// fmt.Sprintf("Cat: %d\n", myValue.meowVolume) | |
// return | |
// case Dog: | |
// fmt.Sprintf("Dog: %d %d\n", myValue.barkVolume, myValue.biteStrength) | |
// return | |
// case Frog: | |
// fmt.Sprintf("Frog: %d\n", myValue.leapHeight) | |
// return | |
// case Llama: | |
// fmt.Sprintf("Llama: %d\n", myValue.spitStrength) | |
// return | |
// } | |
// } | |
func RandomPetWrapper() PetWrapper { | |
var pet PetWrapper | |
switch rand.Intn(3) { | |
case 0: | |
pet.cat = &Cat{meowVolume: int32(rand.Intn(10))} | |
case 1: | |
pet.dog = &Dog{barkVolume: int32(rand.Intn(10)), biteStrength: int32(rand.Intn(10))} | |
case 2: | |
pet.frog = &Frog{leapHeight: int32(rand.Intn(10))} | |
case 3: | |
pet.llama = &Llama{spitStrength: int32(rand.Intn(10))} | |
} | |
return pet | |
} | |
func RandomPetInterface() PetInterface { | |
var pet PetInterface | |
switch rand.Intn(3) { | |
case 0: | |
pet = Cat{meowVolume: int32(rand.Intn(10))} | |
case 1: | |
pet = Dog{barkVolume: int32(rand.Intn(10)), biteStrength: int32(rand.Intn(10))} | |
case 2: | |
pet = Frog{leapHeight: int32(rand.Intn(10))} | |
case 3: | |
pet = Llama{spitStrength: int32(rand.Intn(10))} | |
} | |
return pet | |
} | |
/// Generate pet with random tag from 0-2 and random data | |
func RandomPet() Pet { | |
var pet Pet | |
pet.tag = int32(rand.Intn(3)) | |
switch pet.tag { | |
case 0: | |
cat := Cat{meowVolume: int32(rand.Intn(10))} | |
pet.data = *(*[8]uint8)(unsafe.Pointer(&cat)) | |
case 1: | |
dog := Dog{barkVolume: int32(rand.Intn(10)), biteStrength: int32(rand.Intn(10))} | |
pet.data = *(*[8]uint8)(unsafe.Pointer(&dog)) | |
case 2: | |
frog := Frog{leapHeight: int32(rand.Intn(10))} | |
pet.data = *(*[8]uint8)(unsafe.Pointer(&frog)) | |
case 3: | |
llama := Llama{spitStrength: int32(rand.Intn(10))} | |
pet.data = *(*[8]uint8)(unsafe.Pointer(&llama)) | |
} | |
return pet | |
} | |
func (p *Pet) AsCat() *Cat { | |
if p.tag != 0 { | |
return nil | |
} | |
return (*Cat)(unsafe.Pointer(&p.data)) | |
} | |
func (p *Pet) AsDog() *Dog { | |
if p.tag != 1 { | |
return nil | |
} | |
return (*Dog)(unsafe.Pointer(&p.data)) | |
} | |
func (p *Pet) AsFrog() *Frog { | |
if p.tag != 2 { | |
return nil | |
} | |
return (*Frog)(unsafe.Pointer(&p.data)) | |
} | |
func (p *Pet) AsLlama() *Llama { | |
if p.tag != 3 { | |
return nil | |
} | |
return (*Llama)(unsafe.Pointer(&p.data)) | |
} | |
func NewCat(cat Cat) Pet { | |
return Pet { | |
tag: 0, | |
data: *(*[8]uint8)(unsafe.Pointer(&cat)), | |
} | |
} | |
func NewDog(dog Dog) Pet { | |
return Pet { | |
tag: 1, | |
data: *(*[8]uint8)(unsafe.Pointer(&dog)), | |
} | |
} | |
func NewFrog(frog Frog) Pet { | |
return Pet { | |
tag: 2, | |
data: *(*[8]uint8)(unsafe.Pointer(&frog)), | |
} | |
} | |
func NewLlama(llama Llama) Pet { | |
return Pet { | |
tag: 3, | |
data: *(*[8]uint8)(unsafe.Pointer(&llama)), | |
} | |
} | |
type PetInterface interface { | |
__isPet() | |
add() int32 | |
} | |
func (c Cat) __isPet() {} | |
func (c Cat) add() int32 { | |
return c.meowVolume | |
} | |
func (d Dog) __isPet() {} | |
func (d Dog) add() int32 { | |
return d.barkVolume + d.biteStrength | |
} | |
func (f Frog) __isPet() {} | |
func (f Frog) add() int32 { | |
return f.leapHeight | |
} | |
func (l Llama) __isPet() {} | |
func (l Llama) add() int32 { | |
return l.spitStrength | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
go test -bench=. -benchmem goos: darwin goarch: arm64 pkg: github.com/zackradisic/pets/src BenchmarkWrapperSingle-10 288155892 4.018 ns/op 0 B/op 0 allocs/op BenchmarkSumtypeSingle-10 189918730 6.341 ns/op 0 B/op 0 allocs/op BenchmarkInterfaceSingle-10 127764098 9.335 ns/op 0 B/op 0 allocs/op BenchmarkInterfaceDynamicDispatch-10 58420220 20.44 ns/op 0 B/op 0 allocs/op BenchmarkWrapperArray-10 2549085 479.0 ns/op 378 B/op 10 allocs/op BenchmarkSumtypeArray-10 3109705 395.9 ns/op 120 B/op 0 allocs/op BenchmarkInterfaceArray-10 3004935 399.9 ns/op 186 B/op 3 allocs/op BenchmarkInterfaceArrayDynamicDispatch-10 2897180 413.6 ns/op 186 B/op 3 allocs/op PASS ok github.com/zackradisic/pets/src 13.904s