Last active
October 29, 2020 05:09
-
-
Save diamondburned/c369d13efda5c702a0e59874deee64bd to your computer and use it in GitHub Desktop.
Handler benchmark: linked list vs slice map vs slice
This file contains hidden or 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 main | |
import ( | |
"errors" | |
"reflect" | |
) | |
func main() {} | |
type handler struct { | |
event reflect.Type // underlying type; arg0 or chan underlying type | |
callback reflect.Value | |
isIface bool | |
chanclose reflect.Value // IsValid() if chan | |
} | |
func newHandler(unknown interface{}) (*handler, error) { | |
fnV := reflect.ValueOf(unknown) | |
fnT := fnV.Type() | |
// underlying event type | |
var handler = handler{ | |
callback: fnV, | |
} | |
switch fnT.Kind() { | |
case reflect.Func: | |
if fnT.NumIn() != 1 { | |
return nil, errors.New("function can only accept 1 event as argument") | |
} | |
if fnT.NumOut() > 0 { | |
return nil, errors.New("function can't accept returns") | |
} | |
handler.event = fnT.In(0) | |
case reflect.Chan: | |
handler.event = fnT.Elem() | |
handler.chanclose = reflect.ValueOf(make(chan struct{})) | |
default: | |
return nil, errors.New("given interface is not a function or channel") | |
} | |
var kind = handler.event.Kind() | |
// Accept either pointer type or interface{} type | |
if kind != reflect.Ptr && kind != reflect.Interface { | |
return nil, errors.New("first argument is not pointer") | |
} | |
handler.isIface = kind == reflect.Interface | |
return &handler, nil | |
} | |
func (h handler) not(event reflect.Type) bool { | |
if h.isIface { | |
return !event.Implements(h.event) | |
} | |
return h.event != event | |
} |
This file contains hidden or 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 main | |
import ( | |
"container/list" | |
"fmt" | |
"math/rand" | |
"reflect" | |
"testing" | |
"time" | |
) | |
func init() { rand.Seed(time.Now().UnixNano()) } | |
func newDummyHandler() handler { | |
dumbFn := func(s *string) { | |
fmt.Println(*s) | |
} | |
h, err := newHandler(dumbFn) | |
if err != nil { | |
panic(err) | |
} | |
return *h | |
} | |
func _maybeNewDummyHandlers() []handler { | |
var handlers = make([]handler, 0, 10) | |
for i := 0; i < 10; i++ { | |
var dumbFn interface{} | |
if rand.Intn(2) == 1 { | |
dumbFn = func(s *string) { fmt.Println(*s) } | |
} else { | |
dumbFn = func(s *int) { fmt.Println(*s) } | |
} | |
h, err := newHandler(dumbFn) | |
if err != nil { | |
panic(err) | |
} | |
handlers = append(handlers, *h) | |
} | |
return handlers | |
} | |
func maybeNewDummyHandlers() []handler { | |
return append([]handler(nil), _maybeNewDummyHandlers()...) | |
} | |
var dumbType = reflect.TypeOf(new(int)) | |
func BenchmarkLinkedListAdd(b *testing.B) { | |
var ll = list.New() | |
for i := 0; i < 10000; i++ { | |
ll.PushBack(newDummyHandler()) | |
} | |
h, err := newHandler(func(i *int) { fmt.Println(*i) }) | |
if err != nil { | |
panic(err) | |
} | |
ll.PushBack(*h) | |
b.ResetTimer() | |
for i := 0; i < b.N; i++ { | |
for elem := ll.Front(); elem != nil; elem = elem.Next() { | |
handler := elem.Value.(handler) | |
if handler.not(dumbType) { | |
continue | |
} | |
} | |
} | |
} | |
func BenchmarkSliceMapAdd(b *testing.B) { | |
var cbMap = map[uint64]handler{} | |
var order = []uint64{} | |
var index = uint64(0) | |
for i := 0; i < 10000; i++ { | |
cbMap[index] = newDummyHandler() | |
order = append(order, index) | |
index++ | |
} | |
h, err := newHandler(func(i *int) { fmt.Println(*i) }) | |
if err != nil { | |
panic(err) | |
} | |
cbMap[index] = *h | |
order = append(order, index) | |
index++ | |
b.ResetTimer() | |
for i := 0; i < b.N; i++ { | |
for _, i := range order { | |
handler, ok := cbMap[i] | |
if !ok { | |
continue | |
} | |
if handler.not(dumbType) { | |
continue | |
} | |
} | |
} | |
} | |
func BenchmarkSliceAdd(b *testing.B) { | |
var sl []handler | |
for i := 0; i < 10000; i++ { | |
sl = append(sl, newDummyHandler()) | |
} | |
h, err := newHandler(func(i *int) { fmt.Println(*i) }) | |
if err != nil { | |
panic(err) | |
} | |
sl = append(sl, *h) | |
b.ResetTimer() | |
for i := 0; i < b.N; i++ { | |
for _, handler := range sl { | |
if handler.not(dumbType) { | |
continue | |
} | |
} | |
} | |
} | |
func BenchmarkLinkedListDelete(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
b.StopTimer() | |
var ll = list.New() | |
var elems = make(map[*list.Element]struct{}, 10) | |
for _, handler := range maybeNewDummyHandlers() { | |
elems[ll.PushBack(handler)] = struct{}{} | |
} | |
b.StartTimer() | |
for i := 0; i < 10; i++ { | |
var elem *list.Element | |
for e := range elems { | |
elem = e | |
break | |
} | |
delete(elems, elem) | |
ll.Remove(elem) | |
} | |
} | |
} | |
func BenchmarkSliceMapDelete(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
b.StopTimer() | |
var cbMap = make(map[uint64]handler, 10) | |
var order = make([]uint64, 0, 10) | |
var index = uint64(0) | |
var elems = make(map[handler]struct{}, 10) | |
for _, handler := range maybeNewDummyHandlers() { | |
cbMap[index] = handler | |
order = append(order, index) | |
index++ | |
elems[handler] = struct{}{} | |
} | |
b.StartTimer() | |
for i := 0; i < 10; i++ { | |
var del uint64 | |
for i := range cbMap { | |
del = i | |
break | |
} | |
delete(cbMap, del) | |
for i, ord := range order { | |
if ord == del { | |
order = append(order[:i], order[i+1:]...) | |
break | |
} | |
} | |
} | |
} | |
} | |
func BenchmarkSliceDelete(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
b.StopTimer() | |
var sl = make([]handler, 0, 10) | |
var elems = make(map[handler]struct{}, 10) | |
for _, handler := range maybeNewDummyHandlers() { | |
sl = append(sl, handler) | |
elems[handler] = struct{}{} | |
} | |
b.StartTimer() | |
for i := 0; i < 10; i++ { | |
var del handler | |
for h := range elems { | |
del = h | |
break | |
} | |
for i, handler := range sl { | |
if handler == del { | |
sl = append(sl[:i], sl[i+1:]...) | |
break | |
} | |
} | |
delete(elems, del) | |
} | |
} | |
} |
This file contains hidden or 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
BenchmarkLinkedListAdd-8 4933 253222 ns/op | |
BenchmarkSliceMapAdd-8 1375 815760 ns/op | |
BenchmarkSliceAdd-8 6985 143225 ns/op | |
BenchmarkLinkedListDelete-8 100000 1965 ns/op | |
BenchmarkSliceMapDelete-8 100000 1966 ns/op | |
BenchmarkSliceDelete-8 100000 2252 ns/op |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment