Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.Sign up
In code review https://go-review.googlesource.com/c/34773/, John Doe points out that a type switch in an inner loop can be optimized by pulling it out of the loop, setting an integer, and switching on the integer value instead.
John Doe provided this benchmark: https://play.golang.org/p/6LXF82e6U4
With tip, I get:
These seem like they could be identical. In both cases, it's just a switch on an integer. In the "Inside" case, that integer just happens to be in the first word of an interface value.
I think the first goal should be to lower the cost of type switches to that of integer switches and then we can talk about how to hoist expensive part out of loops. The first goal clearly benefits more than the 2nd because the programmer always have a way to do that themselves.
In go 1.8, the type switch compiles fairly small. The non-empty-interface-to-concrete-type switch compiles to just a few instructions.
generates for the switch
It's not optimal yet (the redundant TESTQ), but it's pretty small. The code is even slightly smaller when switching on empty interfaces.
Possibly for small switches we could get rid of the hash comparison and check the type pointers directly. (The hash enables binary search. We'd have to do linear search if we just used the type pointers.)
For nonempty interfaces, maybe we could even compare the itab with the address of the now-statically-known I/*T itab struct? Then we wouldn't even need to load the type out of the itab.
The more general question, how to pull this stuff out of the loop altogether, is harder. It involves memory operations so it isn't clear lifting is legal just from looking at the SSA form. We'd have to understand that some of the loads are from never-changing memory locations.
For larger switches, given that we already have a compile-time known hash per type, it looks like it would be quite easy to use something along the lines of MRST. In other words, a small sequence of bits in those hashes could be used as perfect hashing to index a small jump table.
When doing i.(T) for non-empty-interface i and concrete type T, there's no need to read the type out of the itab. Just compare the itab to the itab we expect for that interface/type pair. Also optimize type switches by putting the type hash of the concrete type in the itab. That way we don't need to load the type pointer out of the itab. Update #18492 Change-Id: I49e280a21e5687e771db5b8a56b685291ac168ce Reviewed-on: https://go-review.googlesource.com/34810 Run-TryBot: Keith Randall <firstname.lastname@example.org> TryBot-Result: Gobot Gobot <email@example.com> Reviewed-by: Josh Bleecher Snyder <firstname.lastname@example.org> Reviewed-by: David Chase <email@example.com>