mirror of
https://github.com/facebookresearch/faiss.git
synced 2026-10-12 02:13:36 +00:00
Summary: Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5521 Previous diff: D114685755. Now aarch64 passes. `GpuIndexCagra` had two multi-GPU build entry points totalling 16 positional arguments, neither of which fits the `Index` API. This removes one, folds the other into `train()`, and speeds up the build. **Usage.** Listing more than one device in the config selects the multi-GPU build; `train()` routes to it: ```python devices = faiss.Int32Vector() for i in range(8): devices.push_back(i) an = faiss.AllNeighborsCagraConfig() an.n_clusters = 16 # 0 = auto: max(2 * n_devices, 4) an.overlap_factor = 2 # must be >= 2 an.ivf_pq_search_batch_size = 8192 # 0 = cuVS default; caps IVF-PQ memory config = faiss.GpuIndexCagraConfig() config.graph_degree = 32 config.intermediate_graph_degree = 32 config.build_algo = faiss.graph_build_algo_IVF_PQ config.devices = devices # >1 device selects the multi-GPU build config.all_neighbors_params = an index = faiss.GpuIndexCagra(res, d, faiss.METRIC_L2, config) index.train(xb) # xb must stay alive until copyTo() completes cpu_index = faiss.IndexHNSWCagra() cpu_index.base_level_only = True index.copyTo(cpu_index) # required: the GPU index is not searchable on this path ``` Float32-only, does not copy `x`, and leaves `index_` empty so `copyTo()` is the only valid follow-up. Single-GPU behaviour is unchanged when `devices` is empty. **Deleted `trainMultiGpu`.** Sharded SNMG CAGRA produces per-shard graphs with zero cross-shard edges by construction, so it needs post-hoc stitching just to be usable, and it lost to the `all_neighbors` path on both build time and recall. It had no callers outside the benchmark and one test. **Folded `trainAllNeighbors` into `train()`.** Its 6 trailing bare scalars now live in the constructor-time config struct, which is the established Faiss GPU convention: | Old argument | Now | | --- | --- | | `devices` | `GpuIndexCagraConfig::devices`, also the dispatch predicate | | `build_algo` (0/1/2) | `GpuIndexCagraConfig::build_algo` | | `refinement_rate` | `AllNeighborsCagraConfig::refinement_rate` | | `n_clusters`, `overlap_factor`, `ivfpq_search_batch` | new `AllNeighborsCagraConfig` | This also kills a live footgun: the old `int build_algo` used an encoding (0=NN-descent, 1=brute-force, 2=IVF-PQ) that disagreed with the `graph_build_algo` enum in the same header (0=IVF_PQ, 1=NN_DESCENT). Both callers set `config.build_algo` and then passed an unrelated int, and the config field was silently dead. `BRUTE_FORCE` is appended to `graph_build_algo` (at the end, so existing values do not renumber) and the config field is now the single source of truth. **Graph pruning no longer copies the graph off the device.** Previously only detour counting ran on the GPU and pruning plus reverse-graph construction ran on the host, which was 35% of total build time. This part is **temporary and self-removing**. cuVS has no public API to prune a graph that is already on the device: its exported `helpers::optimize` takes host matrices, and the dispatch behind it erases the mdspan accessor to host memory, so cuVS's own device code path is unreachable from outside. Until that is fixed upstream, this reaches into cuVS's internal headers when the build has them available, and otherwise falls back to the public host API -- correct either way, just slower on the fallback. **Open-source builds get the fallback**, since cuVS does not install those headers; the `train()` docs say so. An upstream patch adding a `device_matrix_view` overload to `cuvs::neighbors::cagra::helpers::optimize` is prepared and will be submitted to rapidsai/cuvs. When it ships, the internal include, the build flag guarding it, and the fallback branch all get deleted and every build gets the fast path. At 50M vectors on 8 GPUs the optimize step goes from 119.1s to 1.65s (72x) and end-to-end build->serialize from 8.0 to 5.4 minutes, recall unchanged. **Collapsed the benchmark to one path.** With the stitching approaches gone, `bench_approaches.py` is a single-path tool for validating and tuning the production build, and reports a per-phase build breakdown plus an efSearch sweep of recall and QPS. Deliberately *not* merged into the config: - `ivf_pq_params` / `ivf_pq_search_params` are still not consulted on this path. cuVS derives them from the dataset shape and those derived values beat the static defaults in the faiss structs, so only the knobs cuVS cannot infer are overridden. - `faiss::cagra_build_algo` only has `{IVF_PQ, NN_DESCENT}`, so a `BRUTE_FORCE` config would silently degrade to NN-descent on the single-GPU path; `train_ex()` now rejects it there. `GpuIndexBinaryCagra` shares this config struct and has no multi-GPU build, so it rejects `devices.size() > 1` rather than silently building on one device. Behaviour changes: the default `build_algo` on the multi-GPU path is now `IVF_PQ` (the config default) rather than NN-descent (the old argument default), and `IndexHNSW::num_base_level_search_entrypoints` goes 32 -> 256, which measured better on both recall and QPS at every efSearch. Reviewed By: junjieqi Differential Revision: D116187873 fbshipit-source-id: 265be7dc0670de262cb1a937ca4b2f530578e237
163 lines
8.2 KiB
TOML
163 lines
8.2 KiB
TOML
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
#
|
|
# This source code is licensed under the MIT license found in the
|
|
# LICENSE file in the root directory of this source tree.
|
|
|
|
[build-system]
|
|
# cuVS requires rapids-cmake which needs cmake >= 3.30.4.
|
|
requires = ["scikit-build-core>=0.10", "swig>=4.2,<5", "numpy>=2.0", "cmake>=3.30.4", "ninja"]
|
|
build-backend = "scikit_build_core.build"
|
|
|
|
[project]
|
|
name = "faiss-gpu-cuvs"
|
|
dynamic = ["version"]
|
|
description = "A library for efficient similarity search and clustering of dense vectors (GPU + cuVS/CAGRA support)."
|
|
readme = "README.md"
|
|
license = "MIT"
|
|
license-files = ["LICENSE", "THIRD_PARTY_NOTICES"]
|
|
requires-python = ">=3.11"
|
|
authors = [
|
|
{name = "Meta AI Research"},
|
|
]
|
|
keywords = ["search", "nearest-neighbors", "clustering", "vectors", "similarity", "gpu", "cuda", "cuvs", "cagra"]
|
|
classifiers = [
|
|
"Development Status :: 5 - Production/Stable",
|
|
"Intended Audience :: Developers",
|
|
"Intended Audience :: Science/Research",
|
|
"Operating System :: POSIX :: Linux",
|
|
"Programming Language :: C++",
|
|
"Programming Language :: Python :: 3",
|
|
"Programming Language :: Python :: 3.11",
|
|
"Programming Language :: Python :: 3.12",
|
|
"Programming Language :: Python :: 3.13",
|
|
"Programming Language :: Python :: 3.14",
|
|
"Programming Language :: Python :: 3 :: Only",
|
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
"Environment :: GPU :: NVIDIA CUDA :: 13",
|
|
]
|
|
dependencies = [
|
|
"numpy>=1.25",
|
|
"packaging",
|
|
"nvidia-cuda-runtime>=13.2,<14",
|
|
"nvidia-cublas>=13.2,<14",
|
|
"nvidia-curand>=10.3.7,<11",
|
|
"nvidia-nvjitlink>=13.2,<14",
|
|
"libcuvs-cu13>=26.06,<27",
|
|
"librmm-cu13>=26.06,<27",
|
|
]
|
|
|
|
[project.urls]
|
|
Homepage = "https://github.com/facebookresearch/faiss"
|
|
Documentation = "https://github.com/facebookresearch/faiss/wiki"
|
|
Repository = "https://github.com/facebookresearch/faiss"
|
|
Issues = "https://github.com/facebookresearch/faiss/issues"
|
|
|
|
[tool.scikit-build]
|
|
cmake.build-type = "Release"
|
|
|
|
# Pull version from CMakeLists.txt's project(VERSION ...) — single source of truth.
|
|
metadata.version.provider = "scikit_build_core.metadata.regex"
|
|
metadata.version.input = "CMakeLists.txt"
|
|
metadata.version.regex = '^\s+VERSION\s+(?P<value>[0-9]+\.[0-9]+\.[0-9]+)'
|
|
|
|
# Exclude C++ headers, cmake configs, static libs, and binaries from the wheel.
|
|
# Only the Python package (faiss/) and bundled shared libs remain.
|
|
wheel.exclude = ["include/**", "share/**", "lib/**", "lib64/**", "bin/**"]
|
|
|
|
[tool.scikit-build.cmake.define]
|
|
FAISS_OPT_LEVEL = "dd"
|
|
FAISS_ENABLE_GPU = "ON"
|
|
FAISS_ENABLE_CUVS = "ON"
|
|
FAISS_ENABLE_PYTHON = "ON"
|
|
FAISS_ENABLE_MKL = "OFF"
|
|
FAISS_ENABLE_C_API = "OFF"
|
|
FAISS_ENABLE_EXTRAS = "OFF"
|
|
FAISS_ENABLE_SVS = "OFF"
|
|
FAISS_USE_LTO = "ON"
|
|
# Turing → Hopper SASS + Hopper PTX trailer for forward compat (e.g. Blackwell JIT).
|
|
# CUDA 13 dropped offline compilation for Maxwell/Pascal/Volta, so 70 (V100) is gone
|
|
# and 75 (Turing) is the floor. Drop 72 (Jetson Xavier) for a datacenter-targeted
|
|
# wheel; include 89 (L4/L40/RTX 40xx).
|
|
CMAKE_CUDA_ARCHITECTURES = "75-real;80-real;86-real;89-real;90"
|
|
CMAKE_CUDA_FLAGS_RELEASE = "-O3 -Xfatbin=-compress-all --threads 4 -Xcompiler=-fdata-sections,-ffunction-sections"
|
|
CMAKE_C_FLAGS = "-fdata-sections -ffunction-sections"
|
|
CMAKE_CXX_FLAGS = "-fdata-sections -ffunction-sections"
|
|
CMAKE_SHARED_LINKER_FLAGS = "-Wl,--gc-sections -Wl,--strip-all"
|
|
CMAKE_MODULE_LINKER_FLAGS = "-Wl,--gc-sections -Wl,--strip-all"
|
|
BUILD_TESTING = "OFF"
|
|
BUILD_SHARED_LIBS = "ON"
|
|
# cuVS + transitive deps (raft, rmm, rapids_logger, header-only cutlass/cuco/nvtx3)
|
|
# are found via CMAKE_PREFIX_PATH (environment block below) at the RAPIDS wheel roots,
|
|
# mirroring the conda build — no explicit *_DIR, agnostic to lib/ vs lib64/.
|
|
|
|
# Stable ABI (abi3): one cp311 wheel covers all 3.11+.
|
|
# Floor is 3.11 because the runtime dep libcuvs-cu13>=26.06 (RAPIDS) requires
|
|
# Python >=3.11 — RAPIDS dropped 3.10 in the 26.04 release.
|
|
# GPU is Linux-only; free-threaded Python (cp3*t) cannot use abi3.
|
|
[[tool.scikit-build.overrides]]
|
|
if.platform-system = "linux"
|
|
if.abi-flags = "^$" # Free-threaded Python does not support stable ABI.
|
|
wheel.py-api = "cp311"
|
|
|
|
[tool.cibuildwheel]
|
|
# Pass through the host GPU so the smoke test can actually exercise CUDA + cuVS.
|
|
# Without this, faiss.get_num_gpus() returns 0 inside the container and all
|
|
# GPU tests silently skip — masking real packaging / driver / SASS-arch problems.
|
|
container-engine = { name = "docker", create-args = ["--gpus", "all"] }
|
|
build = "cp311-manylinux_x86_64"
|
|
test-requires = [
|
|
"numpy>=1.25,<3.0",
|
|
"pytest",
|
|
"nvidia-cuda-runtime>=13.2,<14",
|
|
"nvidia-cublas>=13.2,<14",
|
|
"nvidia-curand>=10.3.7,<11",
|
|
"nvidia-nvjitlink>=13.2,<14",
|
|
"libcuvs-cu13>=26.06,<27",
|
|
"librmm-cu13>=26.06,<27",
|
|
]
|
|
test-command = "python -m pytest {project}/tests/test_wheel_smoke_gpu.py -v"
|
|
|
|
[tool.cibuildwheel.linux]
|
|
# manylinux_2_28 ships gcc-toolset-14; CUDA 13.2 supports GCC 14, so install
|
|
# gcc-toolset-14 explicitly and force the build to use it via CC/CXX/CUDAHOSTCXX below.
|
|
# epel-release is needed for swig. Install the FULL cuda-toolkit-13-2 (not just nvcc +
|
|
# narrow dev libs as faiss-gpu does) because cuVS pulls in RAFT which transitively
|
|
# needs cuSOLVER, cuSPARSE, nvrtc, etc. headers — easier to install all than to track
|
|
# the exact list. Wheel size is unaffected since auditwheel excludes all CUDA libs.
|
|
#
|
|
# cuVS dev files (headers + cmake) come from RAPIDS' manylinux PyPI wheels via
|
|
# `pip install --target /opt/cuvs` — ABI-clean, so no libstdc++/libgcc_s/libgomp strip
|
|
# or RMM symlink shim. cp311 interpreter is explicit (manylinux puts no `pip` on PATH).
|
|
manylinux-x86_64-image = "manylinux_2_28"
|
|
before-all = "dnf install -y gcc-toolset-14 epel-release && dnf install -y openblas-devel openblas-openmp swig && dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo && dnf install -y --setopt=obsoletes=0 cuda-toolkit-13-2 && /opt/python/cp311-cp311/bin/python -m pip install --target /opt/cuvs --no-deps --only-binary ':all:' --extra-index-url https://pypi.nvidia.com 'libcuvs-cu13==26.06.*' 'libraft-cu13==26.06.*' 'librmm-cu13==26.06.*' 'rapids-logger'"
|
|
# auditwheel excludes (not vendored; supplied at runtime by nvidia-* / libcuvs-cu13
|
|
# wheels): host driver libcuda, CUDA runtime/math libs (cusparse/cusolver are explicit —
|
|
# under the cuda-toolkit RPM, not /opt/cuvs), and every RAPIDS .so found under /opt/cuvs.
|
|
repair-wheel-command = "bash -c 'EXCLUDES=\"--exclude libcuda.so.1 --exclude libcudart.so.13 --exclude libcublas.so.13 --exclude libcublasLt.so.13 --exclude libcurand.so.10 --exclude libnvJitLink.so.13 --exclude libcusparse.so.12 --exclude libcusolver.so.11\"; for f in $(find /opt/cuvs -name \"lib*.so*\" -printf \"%f\\n\" | sort -u); do EXCLUDES=\"$EXCLUDES --exclude $f\"; done; auditwheel repair -w {dest_dir} {wheel} $EXCLUDES'"
|
|
# Canary: ensure the host GPU is actually visible to the container before tests
|
|
# run. Without this, smoke tests would silently skip on broken --gpus passthrough.
|
|
before-test = "nvidia-smi || (echo 'GPU not visible to container; check container-engine create-args' && exit 1)"
|
|
|
|
# Run abi3audit on Linux to guarantee Stable ABI compliance.
|
|
# We use 'append' so that the auditwheel repair-wheel-command from
|
|
# [tool.cibuildwheel.linux] (with the CUDA + cuVS --exclude flags) still runs first.
|
|
[[tool.cibuildwheel.overrides]]
|
|
select = "*-manylinux*"
|
|
inherit.repair-wheel-command = "append"
|
|
repair-wheel-command = "pipx run abi3audit --strict --report {wheel}"
|
|
|
|
# Build-tool paths. CUDA from /usr/local/cuda-13.2 (NVIDIA RPM convention);
|
|
# GCC pinned to toolset-14 for CUDA 13.2 support.
|
|
# CUDAHOSTCXX must match CXX or nvcc host TUs get an ABI-mismatched libstdc++.
|
|
# CMAKE_PREFIX_PATH points at the RAPIDS wheel roots so find_package(cuvs) resolves.
|
|
# Set as env (not cmake.define) so CMake merges it with scikit-build-core's own path.
|
|
[tool.cibuildwheel.linux.environment]
|
|
CUDA_HOME = "/usr/local/cuda-13.2"
|
|
CUDACXX = "/usr/local/cuda-13.2/bin/nvcc"
|
|
CC = "/opt/rh/gcc-toolset-14/root/usr/bin/gcc"
|
|
CXX = "/opt/rh/gcc-toolset-14/root/usr/bin/g++"
|
|
CUDAHOSTCXX = "/opt/rh/gcc-toolset-14/root/usr/bin/g++"
|
|
CMAKE_PREFIX_PATH = "/opt/cuvs/libcuvs:/opt/cuvs/libraft:/opt/cuvs/librmm:/opt/cuvs/rapids_logger"
|