Skip to content

PATTERN Cited by 1 source

Accumulator instead of early exit

Intent

Replace a data-dependent break or early-exit condition in a loop with an OR-accumulator that captures the same signal, tested once after the loop completes. This eliminates the vectorization-blocking control-flow dependency without losing the information the early-exit provided.

Mechanism

Before (early-exit, blocks vectorization):

for b in &bytes {
    if *b >= 0x80 { break; }  // stop at first non-ASCII
    // ... process
}

After (accumulator, vectorizable):

let mut high_bit_acc: u8 = 0;
for b in &mut bytes {
    high_bit_acc |= *b;  // detect any non-ASCII byte
    // ... process (branchless)
}
if high_bit_acc & 0x80 != 0 {
    // non-ASCII exists somewhere — run fallback
}

The OR-accumulator preserves the high bit of any byte that had it set. After the loop, a single & 0x80 test determines whether any non-ASCII byte was present. The precise location is determined later (via memchr or similar) only when needed.

Why it works

A data-dependent loop exit (break) tells the compiler "the trip count depends on the data." The compiler cannot know how many iterations will execute, so it cannot emit a fixed-width SIMD instruction that processes N elements — it might process elements past the exit point.

The accumulator makes the trip count fixed (always bytes.len()), which is exactly the information the auto-vectorizer needs to emit SIMD.

Measured impact

On GitHub's casefold benchmark (Apple M4, 5.7 KB ASCII): - With early-exit break: 3.1 GiB/s (0 vector instructions) - With OR-accumulator (same processing): >45 GiB/s (41 vector instructions)

(Source: sources/2026-07-31-github-dont-stop-early-case-folding-source-code-at-memory-speed)

Seen in

Last updated · 607 distilled / 1,851 read