Skip to content

Commit 07e997c

Browse files
committed
supply FastInvoker interface for reduce the function call memory consumption
1 parent c5ab7bf commit 07e997c

File tree

2 files changed

+141
-8
lines changed

2 files changed

+141
-8
lines changed

inject.go

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,20 @@ type Invoker interface {
5050
Invoke(interface{}) ([]reflect.Value, error)
5151
}
5252

53+
// FastInvoker represents an interface for fast calling functions external function.
54+
type FastInvoker interface {
55+
// Invoke
56+
Invoke([]interface{}) ([]reflect.Value, error)
57+
}
58+
59+
// IsFastInvoker check interface is FastInvoker
60+
func IsFastInvoker(h interface{}) bool {
61+
if _, ok := h.(FastInvoker); ok {
62+
return true
63+
}
64+
return false
65+
}
66+
5367
// TypeMapper represents an interface for mapping interface{} values based on type.
5468
type TypeMapper interface {
5569
// Maps the interface{} value based on its immediate type from reflect.TypeOf.
@@ -102,18 +116,52 @@ func New() Injector {
102116
// It panics if f is not a function
103117
func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) {
104118
t := reflect.TypeOf(f)
119+
numIn := t.NumIn()
120+
switch v := f.(type) {
121+
case FastInvoker:
122+
return inj.fastInvoke(v, t, numIn)
123+
default:
124+
return inj.callInvoke(f, t, numIn)
125+
}
126+
}
105127

106-
var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func
107-
for i := 0; i < t.NumIn(); i++ {
108-
argType := t.In(i)
109-
val := inj.GetVal(argType)
110-
if !val.IsValid() {
111-
return nil, fmt.Errorf("Value not found for type %v", argType)
112-
}
128+
// fastInvoke fastInvoke call
129+
func (inj *injector) fastInvoke(f FastInvoker, t reflect.Type, numIn int) ([]reflect.Value, error) {
130+
var in []interface{}
131+
if numIn > 0 {
132+
in = make([]interface{}, numIn) //Panic if t is not kind of Func
133+
var argType reflect.Type
134+
var val reflect.Value
135+
for i := 0; i < numIn; i++ {
136+
argType = t.In(i)
137+
val = inj.GetVal(argType)
138+
if !val.IsValid() {
139+
return nil, fmt.Errorf("Value not found for type %v", argType)
140+
}
113141

114-
in[i] = val
142+
in[i] = val.Interface()
143+
}
115144
}
145+
return f.Invoke(in)
146+
}
116147

148+
// callInvoke reflect.Value.Call
149+
func (inj *injector) callInvoke(f interface{}, t reflect.Type, numIn int) ([]reflect.Value, error) {
150+
var in []reflect.Value
151+
if numIn > 0 {
152+
in = make([]reflect.Value, numIn) //Panic if t is not kind of Func
153+
var argType reflect.Type
154+
var val reflect.Value
155+
for i := 0; i < numIn; i++ {
156+
argType = t.In(i)
157+
val = inj.GetVal(argType)
158+
if !val.IsValid() {
159+
return nil, fmt.Errorf("Value not found for type %v", argType)
160+
}
161+
162+
in[i] = val
163+
}
164+
}
117165
return reflect.ValueOf(f).Call(in), nil
118166
}
119167

inject_test.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,88 @@ func Test_Injector_Implementors(t *testing.T) {
181181
So(injector.GetVal(inject.InterfaceOf((*fmt.Stringer)(nil))).IsValid(), ShouldBeTrue)
182182
})
183183
}
184+
185+
//----------Benchmark Injecto rInvoke-------------
186+
187+
func f1InjectorInvoke(d1 string, d2 SpecialString) string {
188+
return "f1"
189+
}
190+
func f2InjectorInvoke(d1 string, d2 SpecialString) string {
191+
return "f2"
192+
}
193+
194+
func f1SimpleInjectorInvoke() {
195+
}
196+
func f2SimpleInjectorInvoke() {
197+
}
198+
199+
// f2InjectorInvokeHandler f2 Invoke Handler
200+
type f2InjectorInvokeHandler func(d1 string, d2 SpecialString) string
201+
202+
func (f2 f2InjectorInvokeHandler) Invoke(p []interface{}) ([]reflect.Value, error) {
203+
ret := f2(p[0].(string), p[1].(SpecialString))
204+
return []reflect.Value{reflect.ValueOf(ret)}, nil
205+
}
206+
207+
type f2SimpleInjectorInvokeHandler func()
208+
209+
func (f2 f2SimpleInjectorInvokeHandler) Invoke(p []interface{}) ([]reflect.Value, error) {
210+
f2()
211+
return nil, nil
212+
}
213+
214+
func BenchmarkInjectorInvokeNative(b *testing.B) {
215+
b.StopTimer()
216+
dep := "some dependency"
217+
dep2 := "another dep"
218+
var d2 SpecialString
219+
d2 = dep2
220+
221+
b.StartTimer()
222+
for i := 0; i < b.N; i++ {
223+
f1InjectorInvoke(dep, d2)
224+
}
225+
}
226+
227+
func BenchmarkInjectorInvokeOriginal(b *testing.B) {
228+
benchmarkInjectorInvoke(b, false, false)
229+
}
230+
func BenchmarkInjectorInvokeFast(b *testing.B) {
231+
benchmarkInjectorInvoke(b, true, false)
232+
}
233+
func BenchmarkInjectorInvokeOriginalSimple(b *testing.B) {
234+
benchmarkInjectorInvoke(b, false, true)
235+
}
236+
func BenchmarkInjectorInvokeFastSimple(b *testing.B) {
237+
benchmarkInjectorInvoke(b, true, true)
238+
}
239+
func benchmarkInjectorInvoke(b *testing.B, isFast, isSimple bool) {
240+
b.StopTimer()
241+
242+
injector := inject.New()
243+
dep := "some dependency"
244+
injector.Map(dep)
245+
dep2 := "another dep"
246+
injector.MapTo(dep2, (*SpecialString)(nil))
247+
248+
var f1 interface{}
249+
var f2 interface{}
250+
if isSimple { //func()
251+
f1 = f1SimpleInjectorInvoke
252+
f2 = f2SimpleInjectorInvokeHandler(f2SimpleInjectorInvoke)
253+
} else { //func(p1, p2) ret
254+
f1 = f1InjectorInvoke
255+
f2 = f2InjectorInvokeHandler(f2InjectorInvoke)
256+
}
257+
injector.Invoke(f1)
258+
injector.Invoke(f2)
259+
260+
b.StartTimer()
261+
for i := 0; i < b.N; i++ {
262+
if isFast {
263+
injector.Invoke(f2)
264+
} else {
265+
injector.Invoke(f1)
266+
}
267+
}
268+
}

0 commit comments

Comments
 (0)