I don't know if anyone cares about another attempt to brute-force generics
into Go 1, but I've written up a proof of concept here:
http://kothar.net/generic-adapters
This uses structs with func fields to emulate methods, and then generates
adapters at runtime between the typed functions and the generic methods:
type Container struct {
T SomeType
Set func(value SomeType) SomeType
}
type Int64Container struct {
T int64
Set func(value int64) int64
}
func myProgram() {
c := &Int64Container{}
generic.Factory(Container{}).Create(c)
}
My intent was to make generics work in the same spirit to interfaces, and
allow any generic implementation using interface{} to be cast to a more
specialised interface with concrete types.
Regardless of whether this is a bad idea or not, one question I ran into is
about how slow the reflective wrappers are compared to optimised
compile-time code.
I know it's common knowledge that reflection is slow, so this isn't a
surprise in general. However, even when the function call signature and
method signature match (apart from the receiver) assigning the
reflect.Method to the func field value adds quite a large overhead compared
to the equivalent compile-time code (60x).
See
https://bitbucket.org/mikehouston/generic/src/0e86e6eccdc9e84b9cfb7b0322c98964d21b0f4e/test/list/list_test.go?at=master&fileviewer=file-view-default#list_test.go-194
func BenchmarkFuncListSet(b *testing.B) {
l := &Int32ListImpl{}
l.Append(1)
list := &Wrapper{}
list.Set = l.Set
b.ResetTimer()
for i := 0; i < b.N; i++ {
list.Set(0, 5)
}
}
func BenchmarkReflectFuncListSet(b *testing.B) {
impl := &Int32ListImpl{}
impl.Append(1)
list := &Wrapper{}
listValue := reflect.Indirect(reflect.ValueOf(list))
implValue := reflect.ValueOf(impl)
method := implValue.MethodByName("Set")
listValue.FieldByName("Set").Set(method)
b.ResetTimer()
for i := 0; i < b.N; i++ {
list.Set(0, 5)
}
}
BenchmarkDirectListSet-8 2000000000 0.53 ns/op
BenchmarkInterfaceListSet-8 500000000 2.98 ns/op
BenchmarkFuncListSet-8 500000000 3.04 ns/op
BenchmarkReflectFuncListSet-8 10000000 185 ns/op
I presume this is because the compiler can generate a much more efficient
inline wrapper function without any additional type checks, while the
reflected method is similar to reflect.MakeFunc in behaviour, and needs to
check the types of all arguments during each invocation.
Any thoughts on how to reduce some of the overhead would be welcome, but
I'm prepared to accept that this is just the cost of making these wrappers
at runtime with the current reflection support.
Mike.
--
You received this message because you are subscribed to the Google Groups
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.