[
https://issues.apache.org/jira/browse/BEAM-3612?focusedWorklogId=161784&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-161784
]
ASF GitHub Bot logged work on BEAM-3612:
----------------------------------------
Author: ASF GitHub Bot
Created on: 01/Nov/18 23:40
Start Date: 01/Nov/18 23:40
Worklog Time Spent: 10m
Work Description: aaltay closed pull request #6893: [BEAM-3612] Add a
benchmark for method invocation methods.
URL: https://github.com/apache/beam/pull/6893
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/sdks/go/pkg/beam/core/runtime/exec/fn_test.go
b/sdks/go/pkg/beam/core/runtime/exec/fn_test.go
index 7dfdb99d9d2..2b1b96ea96a 100644
--- a/sdks/go/pkg/beam/core/runtime/exec/fn_test.go
+++ b/sdks/go/pkg/beam/core/runtime/exec/fn_test.go
@@ -400,3 +400,210 @@ func BenchmarkInvokeFnCallExtra(b *testing.B) {
}
b.Log(n)
}
+
+// Foo is a struct with a method for measuring method invocation
+// overhead for StructuralDoFns.
+type Foo struct {
+ A int
+}
+
+// WhatsA is a method for measuring a baseline of structural dofn overhead.
+func (f *Foo) WhatsA(b int) int {
+ return f.A + b
+}
+
+// WhatsB is a comparable direct function for baseline comparison.
+func WhatsB(b int) int {
+ return 32 + b
+}
+
+// Expclicit Receiver Type assertion shim.
+type callerFooRInt struct {
+ fn func(*Foo, int) int
+}
+
+func funcMakerFooRInt(fn interface{}) reflectx.Func {
+ f := fn.(func(*Foo, int) int)
+ return &callerFooRInt{fn: f}
+}
+
+func (c *callerFooRInt) Name() string {
+ return reflectx.FunctionName(c.fn)
+}
+
+func (c *callerFooRInt) Type() reflect.Type {
+ return reflect.TypeOf(c.fn)
+}
+
+func (c *callerFooRInt) Call(args []interface{}) []interface{} {
+ a := c.fn(args[0].(*Foo), args[1].(int))
+ return []interface{}{a}
+}
+
+// To satisfy reflectx.Func2x1
+func (c *callerFooRInt) Call2x1(a1, a2 interface{}) interface{} {
+ return c.fn(a1.(*Foo), a2.(int))
+}
+
+// Implicit Receiver type assertion shim.
+type callerInt struct {
+ fn func(int) int
+}
+
+func funcMakerInt(fn interface{}) reflectx.Func {
+ f := fn.(func(int) int)
+ return &callerInt{fn: f}
+}
+
+func (c *callerInt) Name() string {
+ return reflectx.FunctionName(c.fn)
+}
+
+func (c *callerInt) Type() reflect.Type {
+ return reflect.TypeOf(c.fn)
+}
+
+func (c *callerInt) Call(args []interface{}) []interface{} {
+ a := c.fn(args[0].(int))
+ return []interface{}{a}
+}
+
+func (c *callerInt) Call1x1(a0 interface{}) interface{} {
+ return c.fn(a0.(int))
+}
+
+// BenchmarkMethodCalls measures the overhead of different ways of
+// "generically" invoking methods.
+//
+// This benchmark invokes methods along several different axes
+// * Implicit or Explicit method Receiver
+// * Pre-wrapped values and pre-allocated slices.
+// * Invocations along the following ways
+// * Indirect via extracting from a reflect.Value.Interface()
+// * Reflect Package (reflect.Value.Call())
+// * Beam's reflectx.Func, and reflectx.FuncNxM interfaces
+// * Beam's default reflection based reflectx.Func shim
+// * A Type assertion specialized reflectx.Func shim
+//
+// The Implicit or Explicit method receiver difference exists because
+// Go's reflect package treats the two cases different, and there are
+// performance implications this benchmark captures. Implicit adds a
+// fixed amount of overhead perf invocation giving a performance penalty
+// to Structural DoFns.
+//
+// PreAlocating slices and values serves as a comparison point for how much
+// overhead not doing these things costs. In particular in wrapping/unwrapping
+// values with reflect.ValueOf and extracting them with reflect.Value's
+// Interface() method.
+func BenchmarkMethodCalls(b *testing.B) {
+ f := &Foo{A: 3}
+ var gi interface{}
+ g := &Foo{A: 42}
+ gi = g
+ gV := reflect.ValueOf(g)
+ fV := reflect.ValueOf(f)
+
+ indirectFunc := reflect.ValueOf(WhatsB).Interface().(func(int) int)
+
+ nrF := fV.Method(0)
+ nrFi := nrF.Interface().(func(int) int)
+ rxnrF := reflectx.MakeFunc(nrFi)
+ rx0x1nrF := reflectx.ToFunc1x1(rxnrF)
+ shimnrF := funcMakerInt(nrFi) // as if this shim were
registered
+ shim0x1nrF := reflectx.ToFunc1x1(shimnrF) // would be MakeFunc0x1 if
registered
+
+ wrF := fV.Type().Method(0).Func
+ wrFi := wrF.Interface().(func(*Foo, int) int)
+
+ rxF := reflectx.MakeFunc(wrFi)
+ rx1x1F := reflectx.ToFunc2x1(rxF)
+ shimF := funcMakerFooRInt(wrFi) // as if this shim were registered
+ shim1x1F := reflectx.ToFunc2x1(shimF) // would be MakeFunc1x1 if
registered
+
+ var a int
+ var ai interface{} = a
+ aV := reflect.ValueOf(a)
+ rvSlice := []reflect.Value{aV}
+ grvSlice := []reflect.Value{gV, aV}
+ efaceSlice := []interface{}{a}
+ gEfaceSlice := []interface{}{g, a}
+
+ tests := []struct {
+ name string
+ fn func()
+ }{
+ {"DirectMethod", func() { a = g.WhatsA(a) }}, // Baseline as
low as we can go.
+ {"DirectFunc", func() { a = WhatsB(a) }}, // For comparison
purposes
+
+ {"IndirectFunc", func() { a = indirectFunc(a) }}, //
For comparison purposes
+ {"IndirectImplicit", func() { a = nrFi(a) }}, //
Measures the indirection through reflect.Value cost.
+ {"TypeAssertedImplicit", func() { ai = nrFi(ai.(int)) }}, //
Measures the type assertion cost over the above.
+
+ {"ReflectCallImplicit", func() { a =
nrF.Call([]reflect.Value{reflect.ValueOf(a)})[0].Interface().(int) }},
+ {"ReflectCallImplicit-NoWrap", func() { a =
nrF.Call([]reflect.Value{aV})[0].Interface().(int) }},
+ {"ReflectCallImplicit-NoReallocSlice", func() { a =
nrF.Call(rvSlice)[0].Interface().(int) }},
+
+ {"ReflectXCallImplicit", func() { a =
rxnrF.Call([]interface{}{a})[0].(int) }},
+ {"ReflectXCallImplicit-NoReallocSlice", func() { a =
rxnrF.Call(efaceSlice)[0].(int) }},
+ {"ReflectXCall1x1Implicit", func() { a =
rx0x1nrF.Call1x1(a).(int) }}, // Measures the default shimfunc overhead.
+
+ {"ShimedCallImplicit", func() { a =
shimnrF.Call([]interface{}{a})[0].(int) }}, // What we're currently
using for invoking methods
+ {"ShimedCallImplicit-NoReallocSlice", func() { a =
shimnrF.Call(efaceSlice)[0].(int) }}, // Closer to what we're using now.
+ {"ShimedCall1x1Implicit", func() { a =
shim0x1nrF.Call1x1(a).(int) }},
+
+ {"IndirectExplicit", func() { a = wrFi(g, a) }},
// Measures the indirection through reflect.Value cost.
+ {"TypeAssertedExplicit", func() { ai = wrFi(gi.(*Foo),
ai.(int)) }}, // Measures the type assertion cost over the above.
+
+ {"ReflectCallExplicit", func() { a =
wrF.Call([]reflect.Value{reflect.ValueOf(g),
reflect.ValueOf(a)})[0].Interface().(int) }},
+ {"ReflectCallExplicit-NoWrap", func() { a =
wrF.Call([]reflect.Value{gV, aV})[0].Interface().(int) }},
+ {"ReflectCallExplicit-NoReallocSlice", func() { a =
wrF.Call(grvSlice)[0].Interface().(int) }},
+
+ {"ReflectXCallExplicit", func() { a = rxF.Call([]interface{}{g,
a})[0].(int) }},
+ {"ReflectXCallExplicit-NoReallocSlice", func() { a =
rxF.Call(gEfaceSlice)[0].(int) }},
+ {"ReflectXCall2x1Explicit", func() { a = rx1x1F.Call2x1(g,
a).(int) }},
+
+ {"ShimedCallExplicit", func() { a = shimF.Call([]interface{}{g,
a})[0].(int) }},
+ {"ShimedCallExplicit-NoReallocSlice", func() { a =
shimF.Call(gEfaceSlice)[0].(int) }},
+ {"ShimedCall2x1Explicit", func() { a = shim1x1F.Call2x1(g,
a).(int) }},
+ }
+ for _, test := range tests {
+ b.Run(test.name, func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ test.fn()
+ }
+ })
+ }
+ b.Log(a)
+}
+
+/*
+@lostluck 2018/10/30 on a desktop machine.
+
+BenchmarkMethodCalls/DirectMethod-12 1000000000
2.02 ns/op
+BenchmarkMethodCalls/DirectFunc-12 2000000000
1.81 ns/op
+BenchmarkMethodCalls/IndirectFunc-12 300000000
4.66 ns/op
+BenchmarkMethodCalls/IndirectImplicit-12 10000000
185 ns/op
+BenchmarkMethodCalls/TypeAssertedImplicit-12 10000000
228 ns/op
+BenchmarkMethodCalls/ReflectCallImplicit-12 3000000
479 ns/op
+BenchmarkMethodCalls/ReflectCallImplicit-NoWrap-12 3000000
451 ns/op
+BenchmarkMethodCalls/ReflectCallImplicit-NoReallocSlice-12 3000000
424 ns/op
+BenchmarkMethodCalls/ReflectXCallImplicit-12 2000000
756 ns/op
+BenchmarkMethodCalls/ReflectXCallImplicit-NoReallocSlice-12 2000000
662 ns/op **Default**
+BenchmarkMethodCalls/ReflectXCall1x1Implicit-12 2000000
762 ns/op
+BenchmarkMethodCalls/ShimedCallImplicit-12 5000000
374 ns/op
+BenchmarkMethodCalls/ShimedCallImplicit-NoReallocSlice-12 5000000
289 ns/op **With specialized shims**
+BenchmarkMethodCalls/ShimedCall1x1Implicit-12 5000000
249 ns/op **Arity specialized re-work of the invoker**
+
+** Everything below requires an overhaul of structural DoFn invocation code,
and regeneration of all included shims. **
+BenchmarkMethodCalls/IndirectExplicit-12 300000000
4.81 ns/op
+BenchmarkMethodCalls/TypeAssertedExplicit-12 50000000
35.4 ns/op
+BenchmarkMethodCalls/ReflectCallExplicit-12 3000000
434 ns/op
+BenchmarkMethodCalls/ReflectCallExplicit-NoWrap-12 5000000
397 ns/op
+BenchmarkMethodCalls/ReflectCallExplicit-NoReallocSlice-12 5000000
390 ns/op
+BenchmarkMethodCalls/ReflectXCallExplicit-12 2000000
755 ns/op
+BenchmarkMethodCalls/ReflectXCallExplicit-NoReallocSlice-12 2000000
601 ns/op
+BenchmarkMethodCalls/ReflectXCall2x1Explicit-12 2000000
735 ns/op
+BenchmarkMethodCalls/ShimedCallExplicit-12 10000000
198 ns/op
+BenchmarkMethodCalls/ShimedCallExplicit-NoReallocSlice-12 20000000
93.5 ns/op
+BenchmarkMethodCalls/ShimedCall2x1Explicit-12 20000000
68.3 ns/op **Best we could do**
+*/
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
Issue Time Tracking
-------------------
Worklog Id: (was: 161784)
Time Spent: 3h 20m (was: 3h 10m)
> Make it easy to generate type-specialized Go SDK reflectx.Funcs
> ---------------------------------------------------------------
>
> Key: BEAM-3612
> URL: https://issues.apache.org/jira/browse/BEAM-3612
> Project: Beam
> Issue Type: Improvement
> Components: sdk-go
> Reporter: Henning Rohde
> Assignee: Robert Burke
> Priority: Major
> Time Spent: 3h 20m
> Remaining Estimate: 0h
>
--
This message was sent by Atlassian JIRA
(v7.6.3#76005)