Eliminating Go bound checks with unsafe
Eliminating Go bound checks with unsafe
Hot path optimization: unsafe pointer arithmetic to eliminate bound checks the Go compiler can't remove, given you can prove they are truly unnecessary.
July 06, 2026
Part of the Optimization catalog series:
When float division beats integer division
How 4 bytes of padding make array clearing 49% faster
Eliminating Go bound checks with unsafe (this post)
Bound checks elimination (BCE) is probably one of the most robust, most productive<br>optimization techniques in the Go world. This is my go-to technique, I think, when<br>I'm starting to optimize any Go hot path. Why is it so robust? Because it reduces<br>number of instructions and number of branches in a hot path. This alone is<br>excellent because it reduces number of wasted cycles, but there are additional<br>benefits on top of that. If your code already experiences cache capacity<br>and/or conflict misses lowering number of instructions can help with those<br>significantly. We are talking about L1 icache, the uop cache and, maybe, frontend branch<br>prediction caches. Also register pressure, BCE can help with that too. While<br>being so robust, bound checks are easy to detect and sometimes relatively<br>easy to eliminate. Long story short, BCE is usually a quick win worth trying<br>first. However sometimes it's not easy to eliminate bound checks with<br>conventional methods and that's where the unsafe techniques enter and this is<br>what this post is about.
First, what are the Bound Checks? Go is a safe language and provides some<br>guarantees, e.g. the guarantee that you can't access out-of-range slice<br>elements. To do that the compiler adds a bunch of assembly code that makes sure<br>the runtime panics when the out-of-range index is accessed. Let me quickly<br>illustrate it by this tiny example:
func load(src []byte, i int) byte {<br>return src[i]
If I compile it with the -B flag which disables bound checks it produces this<br>concise assembly:
0x71d MOVQ AX, 0x8(SP)<br>0x722 MOVZX 0(AX)(DI*1), AX<br>0x726 RET
Assembly with the -B flag removed shows the overhead caused by bound checks:
+ 0x796 PUSHQ BP<br>+ 0x797 MOVQ SP, BP<br>0x79a MOVQ AX, 0x10(SP)<br>+ 0x79f CMPQ DI, BX<br>+ 0x7a2 JAE 0x7aa<br>0x7a4 MOVZX 0(AX)(DI*1), AX<br>+ 0x7a8 POPQ BP<br>0x7a9 RET<br>+ 0x7aa CALL 0x7af [1:5]R_CALL:runtime.panicBounds<br>+ 0x7af NOPL
Of course this assembly diff is over-dramatic. The function is tiny and it was a<br>leaf but enabling BC made it acquire a CALL which caused addition of the<br>PUSHQ/POPQ BP and MOVQ SP, BP. Any function that gets a CALL stops being a<br>leaf and gets those prologue instructions regardless of bound<br>checks. But even ignoring that, my point is that there is still an overhead. But<br>actually, you know what? As I think about it, it's not over-dramatic. If you<br>have tiny function and BCE converts it to leaf which removes the CALL overhead<br>then it's a legit BCE-related optimization.
By the way, you don't really need to search assembly to find bound checks. The<br>compiler can list all the bound checks with this command:
go build -gcflags="-d=ssa/check_bce/debug=1" .
Before we switch to using unsafe let's take a look at a conventional way of<br>dealing with BC. Go compiler often can eliminate bound checks if you<br>"prove" that they are unnecessary. You prove it by accessing the upper/lower<br>bound first before looping over the rest of the range. Here is an example from<br>a real codebase:
func matchLen(a, b []byte, limit int) int {<br>+ a = a[:limit]<br>+ b = b[:len(a)]<br>i := 0<br>- for ; limit >= 8; limit -= 8 {<br>+ for ; i xor := loadU64(a[i:]) ^ loadU64(b[i:])<br>if xor != 0 {<br>return i + bits.TrailingZeros64(xor)/8<br>- i += 8
- for ; limit > 0 && a[i] == b[i]; limit-- {<br>- i++<br>+ for ; i }
return i
Using i in the loop condition makes the compiler eliminate the bound<br>check as it now can provably determine that all a accesses are within the<br>range. b = b[:len(a)] eliminates b-related bound checks in the loop.
With every Go version compiler becomes smarter and smarter about the bound<br>checks elimination. There are often good ways to hint to the compiler about BCE<br>(check out this post if you look<br>for more conventional examples) but sometimes it's not possible (yet, maybe in<br>the next version of Go it will be) to eliminate BC with conventional methods and<br>that's where unsafe enters.
⚠️ Note that we are talking here about the cases where the compiler can't<br>determine that a bound check is unnecessary but the programmer can. If you can't<br>prove that a bound check is unnecessary then don't eliminate it, the compiler inserts<br>it for a good reason. I think this is obvious but needed to note it just in<br>case.
Ok, let's eliminate some bound checks with unsafe now, shall we? I'll use the<br>best example I could find in my brotli<br>library. There is one ubiquitous<br>function used in there that unavoidably brings BC with itself.<br>binary.LittleEndian.Uint32:
// Uint32 returns the uint32 representation of b[0:4].<br>func (littleEndian) Uint32(b []byte) uint32 {<br>_ = b[3] // bounds check hint...