複数 GPU を活かした FAISS 高速化の全体像
1. なぜマルチ GPU か
-
メモリ拡張 ― 1 台の GPU では載り切らない 10 億件規模の埋め込みを保持可能
-
スループット向上 ―
IndexReplicas方式でバッチ検索を分散すると 8 GPU 構成で 6〜7 倍のスピードアップが報告されているgithub.com -
最新 GPU アルゴリズム ― FAISS 1.10 では NVIDIA cuVS/CAGRA を正式採用し,IVF/HNSW 系の GPU 検索をさらに 1.7〜8.1 倍短縮できるengineering.fb.com
2. アーキテクチャ別の選択肢
| 方式 | 仕組み | 長所 | 留意点 |
|---|---|---|---|
| IndexReplicas | 同一インデックスを各 GPU に複製し,クエリを均等に分配 | ほぼ線形に QPS が伸びる/実装が簡単 | 全 GPU にフルサイズを保持 → メモリ総量は変わらない |
| IndexShards | ベクトル集合を GPU 台数で分割して配置 | 1 /N のメモリで巨大コーパスを保持 | k が大きいとマージコスト増.リコール低下を防ぐには nprobe を十分確保 |
| Hybrid (common_ivf_quantizer) | IVF の粗量子化器だけ共有しコードブックはシャード | メモリ節約と計算共有のバランス | FAISS ≥1.7 で common_ivf_quantizer=True が必要 |
index_cpu_to_gpu_multiple はデフォルトで IndexReplicas を生成し,GpuMultipleClonerOptions(shard=True) を渡すと IndexShards に切り替わるgithub.com
3. Python 実装フロー
ラップレベル API: index_cpu_to_all_gpus / index_cpu_gpu_list / index_cpu_to_gpu_multiple_py は GpuResources を自動生成し,co= で微調整できるgithub.com
低レベル API: 自前で StandardGpuResources() を個別生成し GpuResourcesVector + Int32Vector を渡すと GPU ごとのワークスペースサイズや CUDA ストリームを細かく設定できるbge-model.com
4. GpuMultipleClonerOptions の主要フィールド
GpuMultipleClonerOptions の主要フィールド| フィールド | 代表的な値 | 効果 |
|---|---|---|
shard |
True / False |
シャードかレプリカかを決定 |
shard_type |
ShardsBalanced, ShardsRandom |
分割方法(ベクトル数均等/ランダム) |
useFloat16 |
True |
学習済みコードブック/PQ も FP16 に圧縮 |
common_ivf_quantizer |
True |
各シャードで粗量子化器を共有しメモリを削減 |
copyToHostIfOom |
True |
GPU メモリ不足時に自動で CPU へ退避(性能は低下) |
5. パフォーマンスを引き出す Best Practices
-
バッチサイズ – 検索時は 4 K~16 K 単位で投げると GPU が飽和しやすい。
-
ピンメモリ –
faiss::gpu::PinnedMemoryConfigあるいはtorch.cuda.set_device+ Torch tensor をそのまま渡し,CPU↔GPU コピーを最小化。 -
FP16/INT8 量子化 –
useFloat16=TrueまたはIndexIVFPQFastScanを併用し,帯域とメモリを半減。 -
NVLink or PCIe Gen4+ – GPU 間通信⏩
IndexReplicasはクエリのみ転送,IndexShardsは結果マージ時に通信.高速リンクが効果的。 -
更新頻度に応じた再クローン –
index_cpu.add()→index_cpu_to_all_gpus()を超すボリューム更新は,GPU サイドで直接add()するより一度 CPU でバルク追加して再複製した方が安定。 -
スレッド数 – GPU ごとに 1 CPU スレッドで十分(I/O 管理だけ)なため,スレッドを増やしてもほぼ効果なし。
6. よくある落とし穴と対処法
| 症状 | 原因 | 回避策 |
|---|---|---|
| CUDA out-of-memory | インデックス複製で GPU VRAM 枯渇 | シャード方式+FP16/indices_options=INDICES_CPU |
| リコール低下(シャード) | 各シャードが独立に nprobe を行うため探索範囲が狭い | nprobe を GPUs × nprobe_base に拡大 |
| 追加が極端に遅い | cudaMalloc 多発 |
StandardGpuResources.setTempMemory(bytes) で一時領域を事前確保 |
| PyTorch Tensor→FAISS で同期オーバーヘッド | 異なる CUDA ストリーム | resources.setDefaultNullStreamAllDevices() で統一か,明示的に torch.cuda.synchronize() |
7. 今後の展望
-
cuVS/CAGRA 統合により GPU 検索アルゴリズムが増強され,とくに大規模 IVF/HNSW で 2〜8 倍の性能向上が確認されている(FAISS 1.10, 2025-05)engineering.fb.com
-
Faiss-NGT ブランチ(進行中)では GPU-DRAM + NVMe のハイブリッド格納,スケールアウト RPC 対応が議論されており,単一ノードを超える分散 GPU 検索が現実味を帯びている。
まとめ
-
複製 (IndexReplicas) は高 QPS 低レイテンシに最適。
-
シャード (IndexShards) は巨大データセット向けで,
common_ivf_quantizerと併用するとメモリ効率が高い。 -
Python ラッパで
index_cpu_to_all_gpusを呼び,GpuMultipleClonerOptionsを切り替えるだけで実運用が可能。 -
最新の FAISS 1.10 + cuVS に更新し,FP16/NVLink/十分なバッチサイズを確保すると,CPU 比で 50〜100 倍クラスのスループットが期待できる。
ChatGPT4o 生成日:2025/06/18