Last active
July 26, 2018 15:14
-
-
Save patricksuo/606e4874839456cc02335bd1c5045f27 to your computer and use it in GitHub Desktop.
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 reflectimport ( "runtime" | |
"sync" | |
"unsafe" | |
) | |
type Caller struct { | |
v Value | |
//readonly cache | |
numIn int | |
numOut int | |
isVariadic bool | |
frametype *rtype | |
retOffset uintptr | |
framePool *sync.Pool | |
} | |
func NewCaller(v Value) *Caller { | |
c := new(Caller) | |
c.v = v | |
c.numIn = v.typ.NumIn() | |
c.numOut = v.typ.NumOut() | |
c.isVariadic = v.typ.IsVariadic() | |
var ( | |
t = v.typ | |
rcvrtype *rtype | |
) | |
if v.flag&flagMethod != 0 { | |
rcvrtype, t, _ = methodReceiver("call", v, int(v.flag)>>flagMethodShift) | |
} | |
c.frametype, _, c.retOffset, _, c.framePool = funcLayout(t, rcvrtype) | |
return c | |
} | |
func (c *Caller) Call(in []Value) []Value { | |
c.v.mustBe(Func) | |
c.v.mustBeExported() | |
return c.call("Call", in) | |
} | |
func (c *Caller) call(op string, in []Value) []Value { | |
// Get function pointer, type. | |
v := c.v | |
t := v.typ | |
var ( | |
fn unsafe.Pointer | |
rcvr Value | |
rcvrtype *rtype | |
) | |
if v.flag&flagMethod != 0 { | |
rcvr = v | |
rcvrtype, t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift) | |
} else if v.flag&flagIndir != 0 { | |
fn = *(*unsafe.Pointer)(v.ptr) | |
} else { | |
fn = v.ptr | |
} | |
if fn == nil { | |
panic("reflect.Value.Call: call of nil function") | |
} | |
isSlice := op == "CallSlice" | |
n := c.numIn | |
if isSlice { | |
if !c.isVariadic { | |
panic("reflect: CallSlice of non-variadic function") | |
} | |
if len(in) < n { | |
panic("reflect: CallSlice with too few input arguments") | |
} | |
if len(in) > n { | |
panic("reflect: CallSlice with too many input arguments") | |
} | |
} else { | |
if c.isVariadic { | |
n-- | |
} | |
if len(in) < n { | |
panic("reflect: Call with too few input arguments") | |
} | |
if !c.isVariadic && len(in) > n { | |
panic("reflect: Call with too many input arguments") | |
} | |
} | |
for _, x := range in { | |
if x.Kind() == Invalid { | |
panic("reflect: " + op + " using zero Value argument") | |
} | |
} | |
for i := 0; i < n; i++ { | |
if xt, targ := in[i].Type(), t.In(i); !xt.AssignableTo(targ) { | |
panic("reflect: " + op + " using " + xt.String() + " as type " + targ.String()) | |
} | |
} | |
if !isSlice && c.isVariadic { | |
// prepare slice for remaining values | |
m := len(in) - n | |
slice := MakeSlice(t.In(n), m, m) | |
elem := t.In(n).Elem() | |
for i := 0; i < m; i++ { | |
x := in[n+i] | |
if xt := x.Type(); !xt.AssignableTo(elem) { | |
panic("reflect: cannot use " + xt.String() + " as type " + elem.String() + " in " + op) | |
} | |
slice.Index(i).Set(x) | |
} | |
origIn := in | |
in = make([]Value, n+1) | |
copy(in[:n], origIn) | |
in[n] = slice | |
} | |
nin := len(in) | |
if nin != c.numIn { | |
panic("reflect.Value.Call: wrong argument count") | |
} | |
nout := c.numOut | |
// Compute frame type. | |
//frametype, _, retOffset, _, framePool := funcLayout(t, rcvrtype) | |
frametype, retOffset, framePool := c.frametype, c.retOffset, c.framePool | |
// Allocate a chunk of memory for frame. | |
var args unsafe.Pointer | |
if nout == 0 { | |
args = framePool.Get().(unsafe.Pointer) | |
} else { | |
// Can't use pool if the function has return values. | |
// We will leak pointer to args in ret, so its lifetime is not scoped. | |
args = unsafe_New(frametype) | |
} | |
off := uintptr(0) | |
// Copy inputs into args. | |
if rcvrtype != nil { | |
storeRcvr(rcvr, args) | |
off = ptrSize | |
} | |
for i, v := range in { | |
v.mustBeExported() | |
targ := t.In(i).(*rtype) | |
a := uintptr(targ.align) | |
off = (off + a - 1) &^ (a - 1) | |
n := targ.size | |
if n == 0 { | |
// Not safe to compute args+off pointing at 0 bytes, | |
// because that might point beyond the end of the frame, | |
// but we still need to call assignTo to check assignability. | |
v.assignTo("reflect.Value.Call", targ, nil) | |
continue | |
} | |
addr := add(args, off, "n > 0") | |
v = v.assignTo("reflect.Value.Call", targ, addr) | |
if v.flag&flagIndir != 0 { | |
typedmemmove(targ, addr, v.ptr) | |
} else { | |
*(*unsafe.Pointer)(addr) = v.ptr | |
} | |
off += n | |
} | |
// Call. | |
call(frametype, fn, args, uint32(frametype.size), uint32(retOffset)) | |
// For testing; see TestCallMethodJump. | |
if callGC { | |
runtime.GC() | |
} | |
var ret []Value | |
if nout == 0 { | |
// This is untyped because the frame is really a | |
// stack, even though it's a heap object. | |
memclrNoHeapPointers(args, frametype.size) | |
framePool.Put(args) | |
} else { | |
// Zero the now unused input area of args, | |
// because the Values returned by this function contain pointers to the args object, | |
// and will thus keep the args object alive indefinitely. | |
memclrNoHeapPointers(args, retOffset) | |
// Wrap Values around return values in args. | |
ret = make([]Value, nout) | |
off = retOffset | |
for i := 0; i < nout; i++ { | |
tv := t.Out(i) | |
a := uintptr(tv.Align()) | |
off = (off + a - 1) &^ (a - 1) | |
if tv.Size() != 0 { | |
fl := flagIndir | flag(tv.Kind()) | |
ret[i] = Value{tv.common(), add(args, off, "tv.Size() != 0"), fl} | |
} else { | |
// For zero-sized return value, args+off may point to the next object. | |
// In this case, return the zero value instead. | |
ret[i] = Zero(tv) | |
} | |
off += tv.Size() | |
} | |
} | |
return ret | |
} |
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 reflectbench | |
import ( | |
"reflect" | |
"testing" | |
"unsafe" | |
) | |
type myint int64 | |
type Inccer interface { | |
inc() | |
} | |
func (i *myint) inc() { | |
*i = *i + 1 | |
} | |
func BenchmarkReflectCaller(b *testing.B) { | |
i := new(myint) | |
incnReflectCaller(i.inc, b.N) | |
} | |
func BenchmarkReflectMethodCall(b *testing.B) { | |
i := new(myint) | |
incnReflectCall(i.inc, b.N) | |
} | |
func BenchmarkReflectOnceMethodCall(b *testing.B) { | |
i := new(myint) | |
incnReflectOnceCall(i.inc, b.N) | |
} | |
func BenchmarkStructMethodCall(b *testing.B) { | |
i := new(myint) | |
incnIntmethod(i, b.N) | |
} | |
func BenchmarkInterfaceMethodCall(b *testing.B) { | |
i := new(myint) | |
incnInterface(i, b.N) | |
} | |
func BenchmarkTypeSwitchMethodCall(b *testing.B) { | |
i := new(myint) | |
incnSwitch(i, b.N) | |
} | |
func BenchmarkTypeAssertionMethodCall(b *testing.B) { | |
i := new(myint) | |
incnAssertion(i, b.N) | |
} | |
func incnReflectCaller(v interface{}, n int) { | |
c := reflect.NewCaller(reflect.ValueOf(v)) | |
for k := 0; k < n; k++ { | |
c.Call(nil) | |
} | |
} | |
func incnReflectCall(v interface{}, n int) { | |
for k := 0; k < n; k++ { | |
reflect.ValueOf(v).Call(nil) | |
} | |
} | |
func incnReflectOnceCall(v interface{}, n int) { | |
fn := reflect.ValueOf(v) | |
for k := 0; k < n; k++ { | |
fn.Call(nil) | |
} | |
} | |
func incnIntmethod(i *myint, n int) { | |
for k := 0; k < n; k++ { | |
i.inc() | |
} | |
} | |
func incnInterface(any Inccer, n int) { | |
for k := 0; k < n; k++ { | |
any.inc() | |
} | |
} | |
func incnSwitch(any Inccer, n int) { | |
for k := 0; k < n; k++ { | |
switch v := any.(type) { | |
case *myint: | |
v.inc() | |
} | |
} | |
} | |
func incnAssertion(any Inccer, n int) { | |
for k := 0; k < n; k++ { | |
if newint, ok := any.(*myint); ok { | |
newint.inc() | |
} | |
} | |
} |
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
BenchmarkReflectCaller-8 20000000 64.6 ns/op | |
BenchmarkReflectMethodCall-8 10000000 135 ns/op | |
BenchmarkReflectOnceMethodCall-8 10000000 126 ns/op | |
BenchmarkStructMethodCall-8 2000000000 1.67 ns/op | |
BenchmarkInterfaceMethodCall-8 1000000000 2.41 ns/op | |
BenchmarkTypeSwitchMethodCall-8 2000000000 1.23 ns/op | |
BenchmarkTypeAssertionMethodCall-8 2000000000 1.42 ns/op |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment