How slow is using type assertions / type switches in Go, as a method of run-time type discovery?
I've heard that in C/C++ for example, discovering types at run time has bad performance. To bypass that, you usually add type members to classes, so you can compare against these instead of casting.
I haven't found a clear answer for this throughout the www.
Here's an example of what I'm asking about - Is this considered fast when compared to other type checking methodologies (like mentioned above, or others I'm not aware of)?
func question(anything interface{}) { switch v := anything.(type) { case string: fmt.Println(v) case int32, int64: fmt.Println(v) case SomeCustomType: fmt.Println(v) default: fmt.Println("unknown") } }
It weakens Type Safety Because of this, type assertions are considered an anti-pattern or code smell unless the user is absolutely sure what they are doing. For example, in some advanced/fancy use case of type hacking, tooling or when the typings of a third-party library is not accurate.
Type assertions in Golang provide access to the exact type of variable of an interface. If already the data type is present in the interface, then it will retrieve the actual data type value held by the interface. A type assertion takes an interface value and extracts from it a value of the specified explicit type.
A switch is a multi-way branch statement used in place of multiple if-else statements but can also be used to find out the dynamic type of an interface variable.
It is very easy to write a Benchmark test to check it: http://play.golang.org/p/E9H_4K2J9-
package main import ( "testing" ) type myint int64 type Inccer interface { inc() } func (i *myint) inc() { *i = *i + 1 } func BenchmarkIntmethod(b *testing.B) { i := new(myint) incnIntmethod(i, b.N) } func BenchmarkInterface(b *testing.B) { i := new(myint) incnInterface(i, b.N) } func BenchmarkTypeSwitch(b *testing.B) { i := new(myint) incnSwitch(i, b.N) } func BenchmarkTypeAssertion(b *testing.B) { i := new(myint) incnAssertion(i, b.N) } 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() } } }
EDIT Oct. 09, 2019
It appears that the methods demonstrated above are equal and have no advantage over one another. Here are the results from my machine (AMD R7 2700X, Golang v1.12.9):
BenchmarkIntmethod-16 2000000000 1.67 ns/op BenchmarkInterface-16 1000000000 2.03 ns/op BenchmarkTypeSwitch-16 2000000000 1.70 ns/op BenchmarkTypeAssertion-16 2000000000 1.67 ns/op PASS
AND AGAIN:
BenchmarkIntmethod-16 2000000000 1.68 ns/op BenchmarkInterface-16 1000000000 2.01 ns/op BenchmarkTypeSwitch-16 2000000000 1.66 ns/op BenchmarkTypeAssertion-16 2000000000 1.67 ns/op
PREVIOUS RESULTS on Jan. 19, 2015
On my amd64 machine, I'm getting the following timing:
$ go test -bench=. BenchmarkIntmethod 1000000000 2.71 ns/op BenchmarkInterface 1000000000 2.98 ns/op BenchmarkTypeSwitch 100000000 16.7 ns/op BenchmarkTypeAssertion 100000000 13.8 ns/op
So it looks like accessing the method via type switch or type assertion is about 5-6 times slower than calling the method directly or via interface.
I don't know if C++ is slower or if this slowdown is tolerable for your application.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With