定数除算最適化再考1
整数の除算
有限体の実装の話では加減乗算はしていましたが、除算はやってませんでした。
除算を避けるためにMontgomery乗算を紹介していましたが、除算が必要になる場面はどうしてもあります。
ここでは手始めにuint32_tを対象としたコンパイラに組み込まれている定数除算最適化アルゴリズムを紹介します。
定数除算の最適化のアイデア
uint32_t
などの符号なし整数
ここではPythonの除算記号//
を使って
大抵のCPUは整数の論理右シフト命令を持ち、それにより2べきの整数
そこで前もって
そして
1970年代から提案され, Barret Reductionと呼ばれる手法の論文 "Implementing the Rivest Shamir and Adleman Public Key Encryption Algorithm on a Standard Digital Signal Processor"(Barret, CRYPTO'86) が有名です。
近似なのでどううまく
そして"Division by Invariant Integers using Multiplication"(Granlund, Montgomery, SIGPLAN 1994) とその改善版『Hacker's Delight 1st. edition』(Warren, 2002)アルゴリズムがGCC/Clang/Visual Studioなどのコンパイラに組み込まれています(と思われます)。
コンパイラの出力例
コンパイラの出力例をいくつか見てみましょう。
#include <stdint.h>
uint32_t div3(uint32_t x)
{
return x/3;
}
uint32_t div7(uint32_t x)
{
return x/7;
}
; x64
; gcc-14 a.c -S -masm=intel -O3
div3:
mov eax, edi
mov edx, 2863311531
imul rax, rdx
shr rax, 33
ret
div7:
mov eax, edi
imul rax, rax, 613566757
shr rax, 32
sub edi, eax
shr edi
add eax, edi
shr eax, 2
ret
; AArch64
; clang-18 --target=aarch64 -O3 -S a.c
div3:
mov w8, #43691
movk w8, #43690, lsl #16
umull x8, w0, w8
lsr x0, x8, #33
ret
div7:
mov w8, #18725
movk w8, #9362, lsl #16
umull x8, w0, w8
lsr x8, x8, #32
sub w9, w0, w8
add w8, w8, w9, lsr #1
lsr w0, w8, #2
ret
いくつかマジックナンバーが登場しています。
アルゴリズムの詳細は後述しますが、x64のdiv3に現れる 2863311531
は16進数で 0xaaaaaaab
であり、これはAArch64のdiv3の43691 + (43690<<16)
と同じです。
x64のdiv7の613566757
もAArch64のdiv7の18725+(9362<<16)
に等しく、同じ定数除算最適化手法を用いていることが伺われます。
定数の求め方
今回は"Integer division by constatns: optimal bounds" (Lemire, Barlett, Kaser, 2020)を参考にしつつ、定式化しましょう(今回紹介する定理は論文とは見かけが異なりますが同値なものです)。
まず記号を準備します。
非負の整数
それから天下り的に
なぜ、こんな数字を定義するかはこの後説明します。
定理
整数
もし
解説
まず
その
まず定理の証明をしましょう。
証明
このとき
として
M_d=M のとき
仮定
よって
M_d < M のとき
最大値をとる候補は2個
いずれのときも
またこの定理により、
長くなったので、ひとまずここまで。
Discussion
Accuracy of Integer Division Approximate Functions は Floor sum を用いた解法を想定した問題です。 Floor sum は以下のような問題です:
注: Accuracy of Integer Division Approximate Functions においては、上記 Floor sum の入力制約を1\le N\le 10^{18}\simeq 0.87\times 2^{60},\ 1\le M\le 2^{120}\simeq 1.33\times 10^{36},\ 0\le A,B\le 10^{36} 程度に緩めたもの(計算途中は最大で 2^{180} 程度の値が現れる恐れがある)を考慮する必要があります。
Floor sum の大まかな説明としては kyopro_friends さんの説明が解りやすいかと思います。
Floor sum についての他の解説: