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 golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/efcf0a84-4e7d-4241-9f5a-994774a7f14dn%40googlegroups.com.

Reply via email to