From 6917e06269f57792ea1ad6ea79556cc772df48eb Mon Sep 17 00:00:00 2001 From: Marius Orcsik Date: Sat, 4 Jun 2022 16:35:11 +0200 Subject: [PATCH] Added benchmark and comment about generic On/To pair --- README.md | 25 ++++++++++++++++++++++ helpers.go | 11 +++++----- helpers_test.go | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4b9bd17..12ca117 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,28 @@ They are documented accordingly with annotations from the specification. ```go import "github.com/go-ap/activitypub" ``` + +## Note about generics + +The helper functions exposed by the package come in two flavours: +explicit `OnXXX` and `ToXXX` functions corresponding to each type and, +a generic pair of functions `On[T]` and `To[T]`. + +Before using them you should consider that the former comes with a pretty heavy performance penalty: + +``` +goos: linux +goarch: amd64 +pkg: github.com/go-ap/activitypub +cpu: Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz +Benchmark_OnT_vs_On_T/OnObject-8 752387791 1.633 ns/op +Benchmark_OnT_vs_On_T/On_T_Object-8 4656264 261.8 ns/op +Benchmark_OnT_vs_On_T/OnActor-8 739833261 1.596 ns/op +Benchmark_OnT_vs_On_T/On_T_Actor-8 4035148 301.9 ns/op +Benchmark_OnT_vs_On_T/OnActivity-8 751173854 1.604 ns/op +Benchmark_OnT_vs_On_T/On_T_Activity-8 4062598 285.9 ns/op +Benchmark_OnT_vs_On_T/OnIntransitiveActivity-8 675824500 1.640 ns/op +Benchmark_OnT_vs_On_T/On_T_IntransitiveActivity-8 4372798 274.1 ns/op +PASS +ok github.com/go-ap/activitypub 11.350s +``` diff --git a/helpers.go b/helpers.go index 1171566..60bb2c9 100644 --- a/helpers.go +++ b/helpers.go @@ -55,17 +55,16 @@ func OnLink(it LinkOrIRI, fn WithLinkFn) error { return fn(ob) } -func To[T Objects](it Item) (*T, error) { - ob, ok := it.(T) - if !ok { - return nil, fmt.Errorf("invalid cast for object %T", it) +func To[T Item](it Item) (*T, error) { + if ob, ok := it.(T); ok { + return &ob, nil } - return &ob, nil + return nil, fmt.Errorf("invalid cast for object %T", it) } // On handles in a generic way the call to fn(*T) if the "it" Item can be asserted to one of the Objects type. // It also covers the case where "it" is a collection of items that match the assertion. -func On[T Objects](it Item, fn func(*T) error) error { +func On[T Item](it Item, fn func(*T) error) error { if !IsItemCollection(it) { ob, err := To[T](it) if err != nil { diff --git a/helpers_test.go b/helpers_test.go index 00ccff2..772ce63 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -319,3 +319,60 @@ func TestOn(t *testing.T) { }) } } + +var fnObj = func(_ *Object) error { + return nil +} +var fnAct = func(_ *Actor) error { + return nil +} +var fnA = func(_ *Activity) error { + return nil +} +var fnIA = func(_ *IntransitiveActivity) error { + return nil +} + +func Benchmark_OnT_vs_On_T(b *testing.B) { + var it Item + b.Run("OnObject", func(b *testing.B) { + for i := 0; i < b.N; i++ { + OnObject(it, fnObj) + } + }) + b.Run("On_T_Object", func(b *testing.B) { + for i := 0; i < b.N; i++ { + On[Object](it, fnObj) + } + }) + b.Run("OnActor", func(b *testing.B) { + for i := 0; i < b.N; i++ { + OnActor(it, fnAct) + } + }) + b.Run("On_T_Actor", func(b *testing.B) { + for i := 0; i < b.N; i++ { + On[Actor](it, fnAct) + } + }) + b.Run("OnActivity", func(b *testing.B) { + for i := 0; i < b.N; i++ { + OnActivity(it, fnA) + } + }) + b.Run("On_T_Activity", func(b *testing.B) { + for i := 0; i < b.N; i++ { + On[Activity](it, fnA) + } + }) + b.Run("OnIntransitiveActivity", func(b *testing.B) { + for i := 0; i < b.N; i++ { + OnIntransitiveActivity(it, fnIA) + } + }) + b.Run("On_T_IntransitiveActivity", func(b *testing.B) { + for i := 0; i < b.N; i++ { + On[IntransitiveActivity](it, fnIA) + } + }) +}