The Great Equality Overhaul
Keith Randall leads a major compiler refactoring that revolutionizes how Go generates equality functions, reducing binary size by 1% and simplifying the codebase. Meanwhile, performance improvements to random number generation show impressive 42-75% speed boosts, and several compiler optimizations make code generation more efficient.
Duration: PT4M22S
https://podlog.io/listen/go-e282e2e6/episode/the-great-equality-overhaul-c0def5df
Transcript
Hey there, Go developers! Welcome back to another episode of the Go podcast. I'm your host, and wow, do we have an exciting episode for you today. January 24th brought us 27 commits packed with some absolutely fascinating improvements to the Go compiler and runtime.
Now, I have to tell you - today's activity is dominated by what I'm calling "The Great Equality Overhaul," and it's honestly one of those changes that makes me genuinely excited about compiler engineering. Keith Randall has been on an absolute tear, leading this massive restructuring of how Go generates equality functions, and the results are pretty spectacular.
So here's the story - traditionally, Go would generate a separate equality function for every single type that needed comparison. That sounds reasonable until you realize your Go binary was carrying around 634 different equality functions! Keith had this brilliant insight: what if we grouped types by their "signature" - essentially a summary of what comparisons they need?
Picture this - you have a struct with an int32, a uint32, a string, and an interface. Instead of getting its own unique equality function, it now gets tagged with the signature "M8SI" - that's 8 bytes of memory, a string, and a nonempty interface. Any other struct with the same comparison pattern can share that same function. The result? Those 634 equality functions dropped to just 286, and the Go binary shrunk by about 1%. That's not just a win for binary size - it's also cleaner, more maintainable code.
But Keith didn't stop there. In a beautiful display of consistent engineering, he extended this signature-based approach to hash functions too. It's one of those changes where you step back and think, "This is how it should have always worked."
Now, let's talk performance, because Gavin Lam delivered some absolutely stellar improvements to the runtime's random number generation. The current cheaprand function was doing something pretty inefficient - performing 128-bit multiplication just to truncate the result to 32 bits. Gavin's fix? Use a proper 32-bit implementation that does 64-bit multiplication on 32-bit numbers instead.
The numbers here are just beautiful - cheaprand got about 10% faster, cheaprand64 improved by over 42%, and here's the kicker - block sampling, which relies on this randomness, got 75% faster. That's the kind of improvement that makes a real difference in production systems.
We also saw some nice compiler optimizations from Jorropo, including a clever trick for AMD64 where the compiler now uses 32-bit copies instead of 64-bit copies when dealing with 32-bit values. It saves just one byte per operation by avoiding the REX prefix, but these tiny optimizations add up across an entire codebase.
There was also a important bug fix from xieyuschen around loop variable semantics and line directives. You know how Go 1.22 changed loop variable behavior? Well, there was this edge case where line directives could cause the compiler to use the wrong semantics based on your go.mod version. It's the kind of subtle bug that could really trip you up, so it's great to see it fixed.
And of course, we got the usual dependency updates to keep everything fresh and secure.
For today's focus, here's what I want you to take away: first, if you're working with performance-critical code that does a lot of comparisons or uses random number generation, you might want to benchmark your applications with a recent Go build - you could see some nice improvements. Second, if you've been hesitant about the loop variable changes in Go 1.22, know that the toolchain keeps getting more robust around edge cases.
Most importantly, changes like Keith's equality overhaul remind us why thoughtful refactoring matters. Sometimes the biggest wins come not from adding features, but from stepping back and asking, "Is there a fundamentally better way to do this?"
That's a wrap for today's episode. Keep coding, keep learning, and I'll catch you next time with more Go goodness. Until then, happy coding!