Skip to content

Instantly share code, notes, and snippets.

@zackradisic
Last active March 2, 2023 09:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zackradisic/287e81406d251447391731b43f82b53a to your computer and use it in GitHub Desktop.
Save zackradisic/287e81406d251447391731b43f82b53a to your computer and use it in GitHub Desktop.
Sum type benchmarks
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
}
@zackradisic
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment