CD Notes
Complete guide to loop optimization covering loop invariant code motion, induction variables, strength reduction, loop unrolling, loop fusion, and other loop transformations.
Introduction
Loops dominate program execution time — a typical program spends 90% of its time in 10% of its code, mostly loops. Loop optimizations therefore have the highest payoff of any optimization category. Even small per-iteration savings multiply by millions of iterations.
Loop Detection (Natural Loops)
| Dominators | Block A dominates block B if every path from entry to B goes through A. |
| Back edge: An edge B | A where A dominates B (loop back to header). |
| Natural loop of back edge B | A: |
| [B1]─ | [B2]─→[B3]─┘ Back edge: B3→B1 (B1 dominates B3) |
| └── | [B4] B4 is after the loop (exit) |
Loop Invariant Code Motion (LICM)
Move computations that produce the same result every iteration to before the loop:
Induction Variables
| Basic induction variable | i = i + c (directly incremented) |
| Derived induction variable | j = a*i + b (linear function of basic IV) |
| j = 4 * i // derived | j = 4*i + 0 |
| k = j + 100 // derived | k = 4*i + 100 |
| i: incremented by 1 each iteration | basic IV |
| j: defined as 4*i | derived IV (coefficient=4, offset=0) |
| k: defined as j+100 = 4*i+100 | derived IV (coefficient=4, offset=100) |
Strength Reduction for Induction Variables
Replace multiplication with addition using recurrence:
| Before strength reduction | After strength reduction: |
| Key insight | j goes 0, 4, 8, 12, ... |
| Each iteration | j_new = j_old + 4 (just addition!) |
Induction Variable Elimination
After strength reduction, the original induction variable may be eliminable:
| After strength reduction | After IV elimination: |
| Replace with | t1 < 4*n (test derived IV directly) |
| Now i is dead | eliminate it! |
| Saved | one ADD per iteration (for i++) |
Loop Unrolling
Loop Fusion and Fission
Loop Fusion (combining two loops)
Before: After fusion:
for (i=0; i<n; i++) for (i=0; i<n; i++) {
a[i] = b[i] + 1; a[i] = b[i] + 1;
for (i=0; i<n; i++) c[i] = a[i] * 2;
c[i] = a[i] * 2; }
// Two loops, a[i] in cache // One loop, better locality
// between loops → cold // a[i] used right after write
Loop Fission (splitting one loop)
Before: After fission:
for (i=0; i<n; i++) { for (i=0; i<n; i++)
a[i] = b[i] + 1; a[i] = b[i] + 1;
c[i] = d[i] * e[i]; for (i=0; i<n; i++)
} c[i] = d[i] * e[i];
// Many arrays, cache thrash // Each loop fits in cache better
Loop Interchange
Interview Questions
- Q: How does the compiler determine if an expression is loop-invariant?
A: An expression is loop-invariant if all its operands are: (1) defined outside the loop, (2) constants, or (3) themselves defined by loop-invariant expressions. The analysis iterates until a fixed point — marking invariant expressions may reveal that others depending on them are also invariant.
- Q: When is loop invariant code motion unsafe?
A: When the hoisted code might not execute in the original (conditional inside the loop), might fault (division by zero, null dereference), or when the assigned variable is live on some path that doesn't execute the assignment. The compiler must verify dominance and exception safety before hoisting.
- Q: What is the relationship between strength reduction and induction variable elimination?
A: Strength reduction converts multiplications (j=4*i) into additions (j+=4). This may make the original induction variable (i) unnecessary if it was only used for computing derived values and the loop test. IV elimination then removes i, replacing the loop test with a test on the derived variable.
- Q: How much speedup does loop unrolling typically provide?
A: On modern architectures, 10-30% for compute-bound loops (reduced branch overhead, better scheduling). For memory-bound loops, less benefit since memory latency dominates. Excessive unrolling can hurt by causing instruction cache misses or increased register pressure.
- Q: What determines the optimal unroll factor?
A: Factors include: number of available registers (don't spill), instruction cache size (unrolled body must fit), pipeline depth (enough instructions to hide latency), SIMD width (unroll to fill vector registers), and trip count distribution (must handle remainders).
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Loop Optimization Techniques in Compiler Design.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Compiler Design topic.
Search Terms
compiler-design, compiler design, compiler, design, code, optimization, loop, loop optimization techniques in compiler design
Related Compiler Design Topics