liveBook · Manning

Fast-path optimization

As explained in 100 Go Mistakes and How to Avoid Them

A fast path is the execution path for a common case that can complete with relatively little work.

A common mistake is treating inlining as an invisible compiler detail. Fast-path optimization deliberately separates the common, cheap case from the uncommon, expensive case so the common case can be inlined. section 12.7

Mid-stack inlining lets the compiler inline functions that call other functions. If a function is too complex to inline as a whole, extract its slow path into a dedicated function and keep the fast path small enough to fit the inlining budget. section 12.7

Refactoring mutex locking

A mutex's unlocked case succeeds with an atomic compare-and-swap and returns immediately. The contended case previously lived in the same complex Lock method, preventing inlining. Moving that logic into lockSlow lets Lock be inlined: uncontended locking avoids a function-call overhead, while the slow path still pays one call. section 12.7

Apply the technique when:

  • Keep the common path direct and compact, and move complex fallback logic into a separate function. section 12.7
  • Check compiler diagnostics rather than assuming inlining occurred; excessive cost can exceed the compiler's budget. section 12.7
  • Use profiling and the execution tracer to decide whether reducing call overhead matters for the application's workload. section 12.11
func (m *Mutex) Lock() {
    if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
        if race.Enabled {
            race.Acquire(unsafe.Pointer(m))
        }
        return
    }
    m.lockSlow()     #1
}
 
func (m *Mutex) lockSlow() {
    var waitStartTime int64
    starving := false
    awoke := false
    iter := 0
    old := m.state
    for {
        // ...
    }
 
    if race.Enabled {
        race.Acquire(unsafe.Pointer(m))
    }
}

Inspect the refactored Mutex implementation, where Lock retains the fast path and delegates contention to lockSlow. section 12.7

How 100 Go Mistakes and How to Avoid Them covers it

How deep it goes
Explains mid-stack inlining, compiler budgets, and slow-path extraction, but stops short of detailed compiler internals.
Examples
Uses compiler diagnostics, small function calls, and the sync.Mutex Lock and lockSlow implementation.
What it assumes
Assumes Go function calls and compiler optimization basics, while explaining fast and slow paths through concrete Go code.
What you can do after
Apply fast-path inlining by isolating slow logic and checking whether the compiler accepts the resulting call site.
How it connects
Connects fast-path inlining with mid-stack inlining, compiler optimization, heap allocation, profiling, and execution tracing.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 Go Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.