liveBook · Manning

Data hazard

As explained in 100 Go Mistakes and How to Avoid Them

A data hazard is an instruction dependency in which one instruction requires a value produced or modified by another instruction.

A data hazard occurs when one instruction needs a value produced or modified by another, preventing the CPU from executing both simultaneously. The dependent instruction must wait, limiting instruction-level parallelism (ILP). section 12.3

const n = 1_000_000
 
func add(s [2]int64) [2]int64 {
    for i := 0; i < n; i++ {       #1
        s[0]++                     #2
        if s[0]%2 == 0 {           #3
            s[1]++
        }
    }
    return s
}

Trace the loop as three operations: increment s[0], test the new value, and conditionally increment s[1]. The test depends on the first increment, creating a data hazard. section 12.3

The dependency is easy to miss because the code is short. In `s[0]++` followed by `if s[0]%2 == 0`, the condition must observe the updated s[0]. The CPU cannot safely run these operations in parallel, although independent operations may still overlap. section 12.3

Reduce the dependency when the computation permits it:

  • Copy the old value into a temporary, update s[0], and test the old value instead. This changes the condition to `if v%2 != 0`, allowing both increment steps to execute in parallel. section 12.3
  • Treat forwarding as a CPU-level mitigation rather than a complete solution: it bypasses writing to a register but does not remove the underlying dependency. section 12.3
  • Do not confuse data hazards with control hazards from branching or structural hazards from competing for the same resource; those require different analyses. section 12.3

Comparing the loop forms

The original `add` loop increments s[0] and then checks the updated value, so the check waits for the increment. The `add2` loop saves the old value in v, updates s[0], and checks v; the two increment paths can then run in parallel while preserving the intended result. section 12.3

How 100 Go Mistakes and How to Avoid Them covers it

How deep it goes
Explains register dependencies, pipeline consequences, forwarding, and a Go-loop rewrite, but stops short of hardware implementation details.
Examples
Uses register instructions and paired Go loops to contrast dependent increments with a temporary-based rewrite.
What it assumes
Assumes basic instruction-level parallelism and CPU-register concepts while deriving hazards from a concrete loop.
What you can do after
Diagnose data dependencies and restructure eligible Go computations to expose more instruction-level parallelism.
How it connects
Connects data hazards with instruction-level parallelism, control hazards, structural hazards, branch prediction, and forwarding.

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.