Case-Folding at Memory Speed
On July 31, 2026, GitHub engineers Alexander Neubeck and Greg Orzell published a detailed account of how they achieved case-folding of source code at over 45 GiB/s on a single core. The post describes a branch-free loop and byte-space arithmetic that let GitHub case-fold every byte of code search at memory bandwidth, without the early termination that typically slows text processing pipelines.
The result matters for any system that needs to normalize or search large volumes of text. Case-folding is the operation that makes search case-insensitive: UserAccount and useraccount should match. For a code search engine indexing millions of repositories, case-folding is a hot path that runs on every byte of every file. The GitHub team's approach is interesting because it deliberately avoids an optimization that most text processing relies on: stopping early.
Why Early Termination Is Tempting
Most text processing loops include an early exit. If a byte is not an ASCII letter, it does not need case-folding, so the loop can skip it. If a byte is already lowercase, it does not need conversion, so the loop can move on. These conditional branches seem like they should make the loop faster by doing less work.
The GitHub post explains why the opposite is true for this workload. Branch prediction works well when the branch is highly predictable, but source code is mixed: letters, digits, symbols, whitespace, and UTF-8 multi-byte sequences all appear in the same stream. A branch that checks "is this an uppercase ASCII letter" is unpredictable because the answer varies frequently. An unpredictable branch causes a pipeline stall, which costs more cycles than simply processing the byte.
Key insight: An unpredictable branch is more expensive than a predictable no-op. When the data is mixed, processing every byte without branching can be faster than skipping bytes that do not need work.
The Branch-Free Approach
The team's solution is a branch-free loop that processes every byte using arithmetic operations instead of conditional branches. The loop reads a byte, computes whether it is an uppercase ASCII letter, produces the folded byte using arithmetic, and writes the result. The computation happens for every byte, regardless of whether the byte needs folding. No branch means no pipeline stall.
The arithmetic works in byte space. Instead of calling a library function or using a lookup table that might cause cache misses, the team uses integer arithmetic that the compiler can optimize into a few instructions. The operations are simple enough that the CPU's execution units can process them at full throughput, limited only by the speed at which data can be read from memory.
Memory Bandwidth as the Ceiling
The post describes the result as memory-bandwidth-bound. That means the loop is processing data as fast as the memory subsystem can deliver it. The CPU is not waiting for instructions to execute; it is waiting for bytes to arrive. At that point, further optimization of the loop itself would not help, because the bottleneck has moved from compute to memory.
This is a desirable state for a text processing operation. If the loop is memory-bandwidth-bound, it means the compute path is efficient enough that the only remaining limit is the hardware's ability to feed it. The 45 GiB/s figure reported in the post is close to the theoretical memory bandwidth of a single core on the target hardware, which confirms that the loop is running at the hardware limit.
Byte-Space Arithmetic Instead of Tables
Lookup tables are a common optimization for case-folding. A 256-entry table maps each byte to its case-folded equivalent. The table is small enough to fit in L1 cache, and the lookup is a single memory access. But the GitHub team chose arithmetic over tables, and the post explains why.
A table lookup is a memory operation, even if it hits L1 cache. In a loop that is already memory-bandwidth-bound, adding another memory access, even a cached one, can reduce throughput. Arithmetic operations use the CPU's execution units, which are separate from the memory subsystem. By keeping the computation in the execution units and off the memory path, the loop avoids competing with the data stream for memory bandwidth.
What This Means for Text Processing
The GitHub result is specific to case-folding on a workload with mixed ASCII content, but the principle generalizes. When a text processing loop is running on mixed data, the cost of unpredictable branches can exceed the cost of processing every byte. When the loop is already memory-bandwidth-bound, adding memory accesses for lookup tables can reduce throughput. The optimal approach is to keep the computation in the execution units and avoid both branches and table lookups.
For developers building search engines, normalizers, or parsers that process large volumes of text, the lesson is to measure before optimizing. An optimization that seems obviously faster, like skipping bytes that do not need processing, can be slower than processing every byte if the branch that enables the skip is unpredictable. The GitHub team's 45 GiB/s result shows that the naive approach, processing every byte with simple arithmetic, can be faster than a clever approach that tries to avoid work.
References
Performance figures are from GitHub's published benchmarks. Actual throughput depends on hardware, data composition, and compiler optimization. Benchmark on your target hardware before applying these techniques.