Files
faiss/CMakeLists.txt
T
Michael Norris a39e260688 Fold multi-GPU CAGRA build into train(), delete trainMultiGpu (#5521)
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
2026-08-17 12:33:38 -07:00

177 lines
5.6 KiB
CMake

# @lint-ignore-every LICENSELINT
# Copyright (c) Facebook, Inc. and its affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
# =============================================================================
# Copyright (c) 2023-2024, NVIDIA CORPORATION.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied. See the License for the specific language governing permissions and limitations under
# the License.
# =============================================================================
cmake_minimum_required(VERSION 3.24.0 FATAL_ERROR)
set(FAISS_LANGUAGES CXX)
if(FAISS_ENABLE_GPU)
if (FAISS_ENABLE_ROCM)
list(APPEND FAISS_LANGUAGES HIP)
list(PREPEND CMAKE_MODULE_PATH "/opt/rocm/lib/cmake")
list(PREPEND CMAKE_PREFIX_PATH "/opt/rocm")
else()
list(APPEND FAISS_LANGUAGES CUDA)
endif()
endif()
if(FAISS_ENABLE_METAL)
list(APPEND FAISS_LANGUAGES OBJCXX)
endif()
# FAISS_ENABLE_METAL may not yet be in the cache on the first cmake run
# (the option() call is after project()). If the user didn't pass
# -DFAISS_ENABLE_METAL=... on the command line we pre-seed the cache
# now so that OBJCXX is added to the project languages.
# Only default to ON on Apple Silicon (arm64); Intel Macs are not
# supported by the Metal backend.
# Prefer CMAKE_SYSTEM_* (target platform, set by toolchain files for
# cross-compiles) and fall back to CMAKE_HOST_* (build machine).
if(NOT DEFINED FAISS_ENABLE_METAL)
if(DEFINED CMAKE_SYSTEM_NAME)
set(_faiss_metal_sys "${CMAKE_SYSTEM_NAME}")
set(_faiss_metal_proc "${CMAKE_SYSTEM_PROCESSOR}")
else()
set(_faiss_metal_sys "${CMAKE_HOST_SYSTEM_NAME}")
set(_faiss_metal_proc "${CMAKE_HOST_SYSTEM_PROCESSOR}")
endif()
if(_faiss_metal_sys STREQUAL "Darwin" AND _faiss_metal_proc STREQUAL "arm64")
set(FAISS_ENABLE_METAL ON CACHE BOOL
"Enable Metal GPU backend for Apple Silicon.")
list(APPEND FAISS_LANGUAGES OBJCXX)
endif()
endif()
if(FAISS_ENABLE_CUVS)
include(cmake/thirdparty/fetch_rapids.cmake)
include(rapids-cmake)
include(rapids-cpm)
include(rapids-cuda)
include(rapids-export)
include(rapids-find)
rapids_cuda_init_architectures(faiss)
rapids_cuda_init_architectures(pyfaiss)
rapids_cuda_init_architectures(faiss_c_library)
endif()
project(faiss
VERSION 1.15.0
DESCRIPTION "A library for efficient similarity search and clustering of dense vectors."
HOMEPAGE_URL "https://github.com/facebookresearch/faiss"
LANGUAGES ${FAISS_LANGUAGES})
include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 20)
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
# Valid values are "generic", "avx2", "avx512", "avx512_spr", "sve".
option(FAISS_OPT_LEVEL "" "generic")
option(FAISS_ENABLE_GPU "Enable support for GPU indexes." ON)
option(FAISS_GPU_STATIC "Link GPU libraries statically." OFF)
option(FAISS_ENABLE_CUVS "Enable cuVS for GPU indexes." OFF)
option(FAISS_ENABLE_ROCM "Enable ROCm for GPU indexes." OFF)
option(FAISS_ENABLE_MKL "Enable MKL." ON)
option(FAISS_ENABLE_PYTHON "Build Python extension." ON)
option(FAISS_ENABLE_C_API "Build C API." OFF)
option(FAISS_ENABLE_EXTRAS "Build extras like benchmarks and demos" ON)
option(FAISS_USE_LTO "Enable Link-Time optimization" OFF)
if(APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
set(_FAISS_METAL_DEFAULT ON)
else()
set(_FAISS_METAL_DEFAULT OFF)
endif()
option(FAISS_ENABLE_METAL "Enable Metal GPU backend for Apple Silicon."
${_FAISS_METAL_DEFAULT})
option(FAISS_ENABLE_SVS "Enable SVS (Intel(R) Scalable Vector Search) integration." OFF)
set(FAISS_SVS_RUNTIME_VERSION "v0" CACHE STRING "Version of the SVS runtime API to use")
set_property(CACHE FAISS_SVS_RUNTIME_VERSION PROPERTY STRINGS "v0")
if(FAISS_ENABLE_GPU)
if(FAISS_ENABLE_ROCM)
enable_language(HIP)
add_definitions(-DUSE_AMD_ROCM)
find_package(HIP REQUIRED)
find_package(hipBLAS REQUIRED)
set(GPU_EXT_PREFIX "hip")
execute_process(COMMAND ${PROJECT_SOURCE_DIR}/faiss/gpu/hipify.sh)
else ()
set(CMAKE_CUDA_HOST_COMPILER ${CMAKE_CXX_COMPILER})
enable_language(CUDA)
set(GPU_EXT_PREFIX "cu")
endif()
endif()
if(FAISS_ENABLE_CUVS AND NOT TARGET cuvs::cuvs)
find_package(cuvs)
endif()
if(FAISS_ENABLE_CUVS AND NOT TARGET rmm::rmm)
find_package(rmm REQUIRED)
endif()
add_subdirectory(faiss)
if(FAISS_ENABLE_GPU)
add_subdirectory(faiss/gpu)
endif()
if(FAISS_ENABLE_METAL)
if(NOT APPLE)
message(FATAL_ERROR "FAISS_ENABLE_METAL requires Apple (macOS) platform.")
endif()
add_subdirectory(faiss/gpu_metal)
endif()
if(FAISS_ENABLE_PYTHON)
add_subdirectory(faiss/python)
endif()
if(FAISS_ENABLE_C_API)
add_subdirectory(c_api)
endif()
if(FAISS_ENABLE_EXTRAS)
add_subdirectory(demos)
add_subdirectory(benchs)
add_subdirectory(tutorial/cpp)
endif()
# CTest must be included in the top level to enable `make test` target.
include(CTest)
if(BUILD_TESTING)
add_subdirectory(tests)
if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING)
add_subdirectory(perf_tests)
endif()
if(FAISS_ENABLE_GPU)
if(FAISS_ENABLE_ROCM)
add_subdirectory(faiss/gpu/test)
else()
add_subdirectory(faiss/gpu/test)
endif()
endif()
if(FAISS_ENABLE_METAL)
add_subdirectory(faiss/gpu_metal/test)
endif()
endif()