Hi there!
Looking at performance bottlenecks for my implementation of Lua in Go
([1]), I found that type assertions can have a significant cost, which
feels unnecessary to me. I couldn't explain it without quite a lot of
context, which makes my post quite long - sorry about that!
I have a Value type that holds Lua values:
// A Value is a runtime value.
type Value struct {
scalar uint64
iface interface{}
}
As Lua is dynamically typed, values can hold any type. In the Lua runtime
implementation, there are "type assertion" functions to convert a Value to
a specific Go type, among which is the Cont interface type ([2]) which
represents a continuation:
func (v Value) AsCont() Cont {
return v.iface.(Cont)
}
This function is called every time a Lua function is called and turns out
to be costly when many function calls are made. I know exactly what types
implement the Cont interface, so I tried an other implementation of AsCont
as follows:
func (v Value) AsCont2() Cont {
switch cont := v.iface.(type) {
case *GoCont:
return cont
case *LuaCont:
return cont
case *Termination:
return cont
default:
// Only the types above implement the Cont interface
panic("value is not a continuation")
}
}
Here is a benchmark comparing AsCont and AsCont2.
func BenchmarkAsCont(b *testing.B) {
v1 := ContValue(new(GoCont))
v2 := ContValue(new(LuaCont))
v3 := ContValue(new(Termination))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = v1.AsCont()
_ = v2.AsCont()
_ = v3.AsCont()
}
}
func BenchmarkAsCont2(b *testing.B) {
v1 := ContValue(new(GoCont))
v2 := ContValue(new(LuaCont))
v3 := ContValue(new(Termination))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = v1.AsCont2()
_ = v2.AsCont2()
_ = v3.AsCont2()
}
}
$ go test -run='^$' -bench '^(BenchmarkAsCont)'
github.com/arnodel/golua/runtime
goos: darwin
goarch: amd64
pkg: github.com/arnodel/golua/runtime
cpu: Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz
BenchmarkAsCont-8 52798504 20.96 ns/op
BenchmarkAsCont2-8 433032686 2.866 ns/op
PASS
ok github.com/arnodel/golua/runtime 2.908s
There's a very significant difference in this benchmark, and in some "real
tests" benchmarking Lua code I get ~15% speedup, which is pretty good.
Now here are my questions.
Question 1: I *think* that the compiler has all the information necessary
to implement type assertion to the Cont interface as I have, i.e. it knows
only 3 types implement that interface, so could it not do the optimisation
on my behalf?
Question 2: Or is it possible that other Go values can be made at runtime
that would implement this interface but not be one of the three known types
that implement it?
Question 3: Is it possible that there is something dodgy going on with the
benchmarks, with some code being optimised away - if so, how can I check
that?
Thanks in advance for any insights!
--
Arnaud
[1] https://github.com/arnodel/golua
[2] Definition of the Cont interface type:
type Cont interface {
Push(Value)
PushEtc([]Value)
RunInThread(*Thread) (Cont, *Error)
Next() Cont
DebugInfo() *DebugInfo
}
--
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].
To view this discussion on the web visit
https://groups.google.com/d/msgid/golang-nuts/efcf0a84-4e7d-4241-9f5a-994774a7f14dn%40googlegroups.com.