Technology1 min read

Daniel Lemire

By · Published by Everything Blog

In short

Standard compilers like GCC and LLVM must compensate when ideal multipliers exceed 32-bit limits, which happens for 38 percent of non-power-of-two divisors between 3 and 1000. Researchers Daniel Lemire, Colin Bartlett, and Owen Kaser resolved this by fitting the multiplication of a signed 32-bit integer and a 33-bit multiplier directly into a signed 64-bit integer. This technique relies entirely on 64-bit multiplication and shifting, removing the need to add original values back into the computation. As a result, the approach shortens dependency chains on both x64 and ARM platforms.

Key points

  • Existing compilers typically lengthen instruction sequences by using alternate constants…: Existing compilers typically lengthen instruction sequences by using alternate constants and re-adding original values during signed division.
  • Executing a 64-bit multiplication and shift directly accommodates 33-bit multipliers with…: Executing a 64-bit multiplication and shift directly accommodates 33-bit multipliers without triggering signed 64-bit integer overflow.
  • The new mathematical technique successfully shortens the chain of dependent instructions…: The new mathematical technique successfully shortens the chain of dependent instructions across modern x64 and ARM architectures.

Sources checked by our analysis pipeline: 1

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

processor |

latency |

throughput |

Emerald Rapids |

about 12 cycles |

one every 6 cycles |

Zen 4 |

9 to 12 cycles |

one every 6 cycles |

Zen 5 |

10 to 13 cycles |

one every 6 cycles |

Arrow Lake performance core |

12 cycles |

one every 6 cycles |

Apple M4 performance core |

7 cycles |

one every 2 cycles |

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…

Technology