2025/09/24 15:17 Engineering a fixed-width bit-packed integer vector in Rust

やっほー、ロボ子!今日のITニュースは、Rustでの効率的な圧縮整数ベクトルの実装についてじゃ。

博士、こんにちは。圧縮整数ベクトルですか。大規模データセットを扱う際にメモリ使用量を削減する技術ですね。

そうそう!標準の`Vec<T>`だと、値の範囲が型の容量より小さい場合にメモリが無駄になるのじゃ。例えば、値5を`Vec<u64>`に入れると、8バイトのうち3ビットしか使わないことになるぞ。

なるほど。10億個の`u64`要素のベクターで、すべての要素が1バイトに収まる場合でも、約8GBのメモリを消費してしまうんですね。

そこで登場するのがビットパッキングじゃ!データを連続したビットベクターに詰めることで、メモリ使用量を減らすのじゃ。

でも、それだとランダムアクセス性能が犠牲になる可能性があるんですよね?

`FixedVec`というのを使うと、データの範囲が既知の場合に、固定のビット幅で整数を格納できるのじゃ。例えば、最大値が1000なら、10ビットで表現できるぞ。

`FixedVec`は、`N`個の整数(各`bit_width`ビット幅)の論理配列として想像でき、`u64`ワードのバッキングバッファーに格納されるんですね。

その通り!ワード境界を越えるアクセスが発生する場合は、2つの連続する`u64`ワードを読み取って、ビットを結合する必要があるのじゃ。

アラインメントされていないメモリアクセスを使用すると、複数の命令シーケンスをより直接的なものに置き換えることができるんですね。Rustの`read_unaligned`組み込み関数は、効率的なマシン命令にコンパイルされると。

`bit_width`の値が32未満の場合、`FixedVec`の`get_unaligned_unchecked`は、対応する`Vec<T>`ベースラインよりもほぼ常に高速なのじゃ。キャッシュの局所性が向上するからじゃな。

反復処理についても工夫があるんですね。`stateful`イテレーターは、ビットベクター上で直接動作し、独自のポジションを維持すると。

そうじゃ!イテレーターは、バッキングストアから`u64`ワードをウィンドウにロードし、そこから直接値をデコードするのじゃ。

ビットの書き込みは、読み取りと同じ問題を逆方向に引き起こすんですね。隣接するデータを破壊しないように、読み取り-変更-書き込みシーケンスが必要だと。

書き込みの場合、`Vec<T>`ベースラインがほぼすべてのビット幅で明確な勝者なのじゃ。これは覚えておくと良いぞ。

`FixedVec`は、論理型、物理ストレージ型、ビットレベルの順序、および所有権に対してジェネリックなんですね。`Word`トレイトや`Storable`トレイトを使って、柔軟な設計になっていると。

可変APIを提供するために、`MutProxy`という構造体で実装されたプロキシオブジェクトパターンをエミュレートする必要があるのじゃ。

ゼロコピービューを実現するために、`FixedVecSlice`を作成できるんですね。可変スライスの場合、スライス相対インデックスを親ベクトルの絶対インデックスに変換することで、`at_mut`メソッドを実装できると。

今後の課題は、同時アクセスへの対応や、可変長エンコーディングの導入じゃな。データが歪んだ分布の場合、要素ごとのビット数を固定ではなく、可変長にすることで、圧縮率を上げられる可能性があるのじゃ。

勉強になります、博士!しかし、FixedVecの設計は奥が深いですね。

じゃろ?最後に一つなぞなぞじゃ!メモリを圧縮するとどうなる?

えーと…小さくなる、ですか?

正解!そして、もっと仲良くなれるのじゃ!…って、メモリと仲良くなっても嬉しくないか。
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。
