Duff's Device in C: Loop Unrolling and Fall-Through
Duff's device is an algorithmic optimization technique designed to
reduce loop overhead in C by combining loop unrolling with the
fall-through behavior of switch statements. Invented in 1983 by Tom Duff
while optimizing a real-time animation program, it solves the problem of
handling leftover iterations when unrolling loops. By interlacing a
switch statement with a do-while loop, Duff's
device creates a mechanism that jumps directly into the middle of an
unrolled loop on the first pass, executing the exact number of required
operations without needing a separate cleanup loop.
The Problem: Loop Overhead and Remainder Handling
When copying data or executing a repetitive task in C, a standard loop incurs branch condition overhead on every single iteration:
void copy(short *to, short *from, int count) {
do {
*to = *from++;
} while (--count > 0);
}To optimize this, programmers use loop unrolling, repeating the operation multiple times per iteration (for example, eight times) to reduce condition checks by a factor of eight.
However, if count is not an exact multiple of eight, an
unrolled loop cannot handle all elements cleanly. Traditional solutions
require a secondary loop or a separate switch block before
or after the unrolled loop to process the remaining elements (the
modulo: count % 8). This creates code duplication and
additional branch instructions.
The Mechanism: C Syntax Exploitation
Duff's device relies on two specific, valid syntactic features of the C language:
- Relaxed Control Flow Nesting: In C, a
switchstatement does not create an isolated scope for jumps. Acaselabel can be placed inside inner control structures, such asif,while, ordo-whileblocks, as long as they reside within the enclosing switch block. - Fall-Through Behavior: Unlike modern languages, C
switch statements do not automatically break after executing a matching
case. Execution continues sequentially down through all following cases until abreakor loop boundary is encountered.
The Implementation
Tom Duff combined these mechanisms into the following pattern:
void send(register short *to, register short *from, register int count) {
register int n = (count + 7) / 8;
switch (count % 8) {
case 0: do { *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while (--n > 0);
}
}How the Device Works Step-by-Step
- Batch Calculation: The variable
ncomputes the total number of passes through thedo-whileloop using integer division:(count + 7) / 8. Ifcountis 20,nbecomes(20 + 7) / 8 = 3full passes. - The Initial Jump: The
switch (count % 8)evaluates the remainder. Ifcountis 20,20 % 8 = 4. The program executes a computed jump directly tocase 4. - Partial Execution (Fall-Through): Because there are
no
breakstatements, execution falls throughcase 4,case 3,case 2, andcase 1. This processes exactly four items—the remainder of the division. - The Loop Boundary: After
case 1, the program reacheswhile (--n > 0). The counterndecrements from 3 to 2. Because2 > 0is true, control loops back to the top of thedoblock. - Full Unrolled Iterations: The top of the
doblock starts atcase 0. The remaining two passes process all eight assignments (case 0down throughcase 1) in sequence without jumping, processing the remaining 16 items. - Termination: When
nreaches 0, thedo-whilecondition fails, and execution exits both the loop and the switch construct cleanly.
Modern Relevance
While Duff's device remains one of the most famous C idioms, modern optimizing compilers (such as GCC and Clang) typically eliminate the need for it. Modern compilers can automatically unroll loops, perform SIMD vectorization, and generate target-specific assembly that outperforms manual source-level unrolling. Furthermore, the convoluted jump targets of Duff's device can disrupt instruction pipelining and branch prediction units on modern architectures. Nonetheless, it stands as an ingenious example of exploiting C's low-level structural grammar.