[ https://issues.apache.org/jira/browse/BEAM-14347?focusedWorklogId=769224&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-769224 ]
ASF GitHub Bot logged work on BEAM-14347: ----------------------------------------- Author: ASF GitHub Bot Created on: 11/May/22 18:05 Start Date: 11/May/22 18:05 Worklog Time Spent: 10m Work Description: lostluck commented on code in PR #17613: URL: https://github.com/apache/beam/pull/17613#discussion_r870605529 ########## sdks/go/pkg/beam/register/register_test.go: ########## @@ -672,3 +675,133 @@ func (fn *PartialCombiner2) AddInput(i int, c CustomType) int { func (fn *PartialCombiner2) MergeAccumulators(i1 int, i2 int) int { return i1 + i2 } + +// Foo is a struct with a method for measuring method invocation +// overhead for StructuralDoFns. +type Foo struct { + A int +} + +// ProcessElement is a method for measuring a baseline of structural dofn overhead. +func (f *Foo) ProcessElement(b CustomType) int { + return f.A + b.val +} + +func MakeMultiEdge(f *graph.DoFn) graph.MultiEdge { + return graph.MultiEdge{ + DoFn: f, + } +} + +type callerCustomTypeГint struct { + fn func(CustomType) int +} + +func funcMakerCustomTypeГint(fn interface{}) reflectx.Func { + f := fn.(func(CustomType) int) + return &callerCustomTypeГint{fn: f} +} + +func (c *callerCustomTypeГint) Name() string { + return reflectx.FunctionName(c.fn) +} + +func (c *callerCustomTypeГint) Type() reflect.Type { + return reflect.TypeOf(c.fn) +} + +func (c *callerCustomTypeГint) Call(args []interface{}) []interface{} { + out0 := c.fn(args[0].(CustomType)) + return []interface{}{out0} +} + +func (c *callerCustomTypeГint) Call1x1(arg0 interface{}) interface{} { + return c.fn(arg0.(CustomType)) +} + +func wrapMakerFoo(fn interface{}) map[string]reflectx.Func { + dfn := fn.(*Foo) + return map[string]reflectx.Func{ + "ProcessElement": reflectx.MakeFunc(func(a0 CustomType) int { return dfn.ProcessElement(a0) }), + } +} + +func GeneratedOptimizationCalls() { + runtime.RegisterType(reflect.TypeOf((*Foo)(nil)).Elem()) + schema.RegisterType(reflect.TypeOf((*Foo)(nil)).Elem()) + runtime.RegisterType(reflect.TypeOf((*CustomType)(nil)).Elem()) + schema.RegisterType(reflect.TypeOf((*CustomType)(nil)).Elem()) + reflectx.RegisterFunc(reflect.TypeOf((*func(CustomType) int)(nil)).Elem(), funcMakerCustomTypeГint) + reflectx.RegisterStructWrapper(reflect.TypeOf((*Foo)(nil)).Elem(), wrapMakerFoo) +} + +// BenchmarkMethodCalls measures the overhead of invoking several different methods after performing +// different types of registration. The unoptimized calls don't perform any optimization. The +// GenericRegistration calls first register the DoFn being used with this package's generic registration +// functions. This is the preferred path for users. The GeneratedShims calls call various registration +// functions, mirroring the behavior of the shims generated by the code generator. This is not the +// recommended path for most users - if these are materially better than the generic benchmarks, +// this package requires further optimization. +// +// BenchmarkMethodCalls/MakeFunc_Unoptimized-16 11480814 88.35 ns/op +// BenchmarkMethodCalls/NewFn_Unoptimized-16 875199 1385 ns/op +// BenchmarkMethodCalls/EncodeMultiEdge_Unoptimized-16 1000000 1063 ns/op +// +// BenchmarkMethodCalls/MakeFunc_GenericRegistration-16 16266259 72.07 ns/op +// BenchmarkMethodCalls/NewFn_GenericRegistration-16 1000000 1108 ns/op +// BenchmarkMethodCalls/EncodeMultiEdge_GenericRegistration-16 1000000 1052 ns/op +// +// BenchmarkMethodCalls/MakeFunc_GeneratedShims#01-16 16400914 69.17 ns/op +// BenchmarkMethodCalls/NewFn_GeneratedShims#01-16 1000000 1099 ns/op +// BenchmarkMethodCalls/EncodeMultiEdge_GeneratedShims#01-16 1000000 1071 ns/op +func BenchmarkMethodCalls(b *testing.B) { Review Comment: So none of these actually test "calling" the DoFns and similar. This is just measuring all the existing pipeline construction time + bundle unmarshal time scaffolding, that would not be meaningfully affected by any of the generated code or not. It's nice to validate that things are lightly faster this way or at least comparable, but it doesn't demonstrate the value of the wrappings, or that the generic approach is equivalent to the code generated approach. To test the generated code, one would need to be calling `aFunc.Call([]interface{CustomType{ A: 4 }})` or similar. And likely moving the parameter definition out of the loop so it's not bogged down by unrelated slice allocation overhead, or also having a `a1x1Func` (of type `reflectx.Func1x1`) so `Call1x1(CustomType{ A: 4 })` could be used.) Issue Time Tracking ------------------- Worklog Id: (was: 769224) Time Spent: 15h (was: 14h 50m) > [Go SDK] Allow users to optimize DoFns with a single generic registration > function > ---------------------------------------------------------------------------------- > > Key: BEAM-14347 > URL: https://issues.apache.org/jira/browse/BEAM-14347 > Project: Beam > Issue Type: New Feature > Components: sdk-go > Reporter: Danny McCormick > Assignee: Danny McCormick > Priority: P2 > Time Spent: 15h > Remaining Estimate: 0h > > Right now, to optimize DoFn execution, users have to use the code generator. > This updates to allow them to use generics instead. -- This message was sent by Atlassian Jira (v8.20.7#820007)