静的探索木は二分探索よりも40倍高速 – 最適化のディープダイブ

静的探索木は二分探索よりも40倍高速 – 最適化のディープダイブ

TL;DR – 静的探索木は二分探索をスループットで最大40倍上回る

静的探索木(S+ trees)は、ソート済み32ビット整数配列に対する下限クエリを、Rust標準ライブラリの二分探索よりも最大40倍高速に回答でき、4GBのデータセットでクエリあたり27nsに達します。この高速化は、キャッシュを意識したノードレイアウト、SIMDベースのfind、積極的なバッチ処理、プリフェッチ、ポインタ演算のトリック、およびクエリレイヤーのインターリーブによって実現されます。


1. 問題とベースライン

目標. ソート済みのVec<u32>とクエリのバッチが与えられたとき、各クエリに対してその値以上の最小の要素(またはu32::MAX)を返します。指標はスループット – クエリあたりのナノ秒数として測定される秒間クエリ数です。

ベースライン. 単純な配列に対するRustのbinary_searchと、Eytzingerレイアウト(幅優先順序で格納された二分木)が参照点として使用されました。1GBの入力において、Eytzingerレイアウトはキャッシュラインを4つ先までプリフェッチできるため、二分探索よりも約6倍高速です(≈200ns/query vs 1150ns/query)。


2. SIMD加速されたノード探索 (find)

線形スキャンは遅い

Aノード内の16個のソート済み値に対する単純な線形スキャンは、1GBのデータセットで約240ns/queryかかり、Eytzingerベースラインよりも遅くなります。

自動ベクトル化が分岐を排除する

値を< qとしてカウントすることで、早期リターンを避けることができ、コンパイラがAVX2 SIMD比較、popcounts、およびpopcnt命令を生成できるようになります。このバージョンは線形スキャンよりも2倍高速であり、Eytzingerレイアウトを上回ります。

""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""- The auto-vectorized version is over two times faster than the[] linear find, and now clearly beats Eytzinger layout!" – author’s comment.

手動 SIMD と popcnt

手書きのイントリンジックを使用して、コンパイラ生成のpopcntプラスtzcntシーケンスを、単一のvpmovmskbに続いてpopcntへと置き換えます。最終的なfind_popcnt関数は、ノード探索における命令数を5命令に削減し、S-treeに対して115ns/queryを実現し、自動ベクトル化バージョンよりも2倍の高速化をもたらします。


--- 3. クエリのバッチ処理

\

Sources