- CPU命令3つだけでうるう年を判定する関数を実装
- この方法はビット演算と乗算を使い、従来の分岐なしで動作
- この方式は0〜102499年の範囲で正確
- ベンチマーク結果では既存手法と比べて約3.8倍高速
概要と問題提起
- 0年から102499年までの年に対して、わずか3つのCPU命令だけを使ってうるう年を判定する関数
- 実際に使われる関数は
((y * 1073750999) & 3221352463) <= 126976 という構造
- このビットツイードリング(bit-twiddling)手法の原理と動作、実用上の有効性を説明
従来のうるう年判定方法
- 通常、うるう年判定は剰余(modulo)と条件分岐で実装する
標準的なアプローチの最適化
(y % 100) の検査を (y % 25) に、(y % 400) の検査を (y % 16) に置き換え可能
- その理由は、先に4で割っているため、25および16との乗算関係に変換できるから
(y % 25) 演算を除算なしで乗算と比較に変換するマジック定数
- 例:
x * 3264175145 > 171798691 に変換可能
- ビットマスクを加えることで
(y & 3) や (y & 15) により4または16での除算判定を代替可能
- コンパイラはこうした変換を自動化するが、他の言語でも手動で活用できる
分岐なし(branchless)実装法
マジック定数の探索: ビットツイードリング手法
- うるう年判定式をさらに単純化するため、組合せ探索とSMT Solverである Z3 を活用
- 形式:
((y * f) & m) <= t
- 条件を満たす定数 f, m, t をZ3で探索
- 0〜102499の範囲で正確な結果を得られる値を発見
- 最終結果は
(y * 1073750999) & 3221352463 <= 126976
関数の原理と内部構造の解説
- 定数を2進数で分析し、3つの主要ビット領域 A, B, C に区分
- 各領域のビット状態によって、うるう年判定の3つの条件を包含
- 関数の論理的な分解:
- A領域:
(y % 4) != 0 かどうかをはじめとする4の倍数条件の確認
- B領域:
(y % 100) != 0 かどうかを、さまざまなパターン(例: 下2桁が14、57、71など)でフィルタリング
- C領域:
(y % 16) == 0、つまり16の倍数かどうかの確認
- 乗算によって実際に剰余計算なしで複数の条件を組み合わせる原理を解説
- マジック定数を掛けると、100の倍数などで特徴的なビットパターンが形成される
- さらに、乗算誤差や複数桁の数字パターンが現れる数学的な内部構造の分析も含まれる
追加の範囲とビット幅拡張の可能性
- 64ビットへ拡張する場合の適切なマジック定数の組合せ探索法も提示
f, m, t の値をさまざまに変えながら最長範囲を探せる
- StackExchangeでも最適な組合せやZ3を用いた証明例がある
ベンチマークと実際の性能比較
- ベンチマーク結果:
- 2025年のような予測しやすい値では0.65〜0.69nsで差はほとんどない
- ランダム入力では
is_leap_year_fast が約3.8倍高速
- 入力パターンによって分岐(branching)方式の予測が難しい場合に大きな利点がある
結論と実際の適用可否
- 実際の応用では値が予測可能なら利点は小さいが、大量のランダムデータ環境では非常に有用
- 実際にPythonやC#などの標準ライブラリを置き換える際には、現実的なプログラム全体のベンチマークが必要
- アイデアと実装方法そのものが興味深く、特定の状況では性能面で魅力的な解決策となる
2件のコメント
Fast inverse sqrt を思い出させる文章
Hacker Newsの意見
is_leap_year1のようなコードをis_leap_year2のように自動で最適化してくれるという事実に驚いた。なので、Cのソースコード段階で自分で最適化する必要はあまりないのではと思うが、他の言語では依然として有用かもしれない。特にcalプログラムの最新版ソースコードで、うるう年チェックを非常に簡潔に処理している点が印象的だった((y * 1073750999) & 3221352463) <= 126976というコードがどう動くのかの説明が複雑にならざるをえないことには、まったく驚きはない。むしろ当然だと思うCMP(X, Y)マクロ、signum関数の最適化例、Motorola 68000向けアセンブリコード例へのリンク、OpenSolaris由来のビットマクロ集など、多様な最適化手法のリンクが紹介されていた。Open Solarisのブログが消えてしまったことへの残念さにも触れつつ、コード最適化に関心のある人にはおすすめとのこと