Technology1 min read

Daniel Lemire

By · Published by Everything Blog

In short

Compilers like GCC and LLVM traditionally rely on algorithms that require adding the original dividend back into the product when dividing signed 32-bit integers by certain constants. Daniel Lemire, Colin Bartlett, and Owen Kaser demonstrated that wide 64-bit registers allow the calculation to complete without this correction phase. A concise template function yields a shorter dependency chain, eliminating the post-multiplication addition on x64 and ARM platforms. Testing with 16,384 random signed integers verified that the technique lowers latency across multiple compilers and chips.

Key points

  • Modern 64-bit hardware registers enable compilers to bypass conventional multi-instructio…: Modern 64-bit hardware registers enable compilers to bypass conventional multi-instruction correction steps for signed 32-bit division.
  • Removing post-multiplication additions creates shorter dependency chains that measurably…: Removing post-multiplication additions creates shorter dependency chains that measurably improve latency on both Intel and Apple processors.
  • Under Clang 21, the new template implementation dropped calculation latency from up to 10…: Under Clang 21, the new template implementation dropped calculation latency from up to 10.6 cycles down to 8 cycles on Emerald Rapids hardware.

Sources checked by our analysis pipeline: 1

Dividing integers is slow on our processors. A 32-bit division costs:

On Zen 4 and Zen 5, the latency depends on the inputs. The x86 rows are idiv

(uops.info). The Apple row is sdiv

(measurements).

A multiplication has a latency of 3 cycles. Emerald Rapids can do one every cycle. Zen 5 and Arrow Lake do three 64-bit multiplications per cycle (uops.info).

Thankfully, when the divisor is a constant, compilers replace the division by a multiplication and a shift (Granlund and Montgomery, 1994). Dividing by 7 is like multiplying by 1/7. In integer arithmetic, we multiply by an integer close to and we divide by with a shift.

Can we do better than the compiler? With signed 32-bit integers on a 64-bit system, often yes.

The compiler’s approach

Let us divide a signed 32-bit integer by 7:

int32_t div7(int32_t x) { return x / 7; }

GCC 14 produces the following code on an x64 processor:

movsx rax, edi

imul rax, rax, -1840700269

shr rax, 32

add eax, edi

sar edi, 31

sar eax, 2

sub eax, edi

ret

It…

Technology