Files
faiss/c_api/CMakeLists.txt
T
Michael Norris 6bbb068ad8 Measure 20-byte Hamming codes eight at a time (#5587)
Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5587

**TL;DR:** Adds a batched 20-byte Hamming kernel that measures eight codes per call, the only thing that helps the 160-bit width, worth 1.21x end to end.

A 20-byte code has no vector kernel at any x86 SIMD level. `HammingComputer20` measures one distance with three scalar popcounts at every level, including `AVX512_VPOPCNT`, so a scan of 160-bit codes runs the same code everywhere.

Vectorizing a single 20-byte distance does not help. A masked 256-bit load plus `vpopcntq` plus a horizontal reduction measures the same as the scalar body, because the reduction costs more than the three popcounts it replaces.

Measuring several codes per call does help, because it amortizes that reduction. This adds `hamming_batch()` to `HammingComputer20` at `AVX512_VPOPCNT`, which measures eight codes at once:

- Eight codes span 160 bytes. `_mm512_popcnt_epi8` gives a count per byte.
- `_mm512_sad_epu8` sums each aligned 8-byte group.
- A 20-byte code covers two whole groups plus half of a group it shares with its neighbour, so each code needs one scalar popcount for its half.

`IVFBinaryScannerL2::scan_codes` and `IVFBinaryScannerL2::scan_codes_range` call `hamming_batch()` when the computer declares a `batch_size`, then measure the remaining codes one at a time. A computer without a `batch_size` keeps the existing loop, so no other code size changes.

The scanner holds the tiled query, and the constructor rejects a code size that does not match the computer. A caller therefore cannot reach the batch kernel with a stride the kernel does not read.

The byte-wise popcount needs `AVX512_BITALG`, which is a separate CPUID bit from `AVX512_VPOPCNTDQ`. This adds the flag to the `AVX512_VPOPCNT` level and requires both bits to select it. Every CPU that has VPOPCNTDQ also has BITALG, so no CPU loses the level.

An alternative byte-wise popcount built from `vpshufb`, which needs neither bit and would run at the plain `AVX512` level, measures only 1.08x against 1.54x, so it is not worth the wider reach.

Reviewed By: alibeklfc

Differential Revision: D118971285

fbshipit-source-id: 5fd4637c972e5c55e2695b32b9b15fba8181a242
2026-09-10 09:32:36 -07:00

173 lines
6.4 KiB
CMake

# 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.
cmake_minimum_required(VERSION 3.17 FATAL_ERROR)
project(faiss_c_library LANGUAGES C CXX)
set(CMAKE_C_STANDARD 11)
set(FAISS_C_SRC
AutoTune_c.cpp
Clustering_c.cpp
IndexFlat_c.cpp
IndexHNSW_c.cpp
IndexIVFFlat_c.cpp
IndexIVF_c.cpp
IndexLSH_c.cpp
IndexPreTransform_c.cpp
VectorTransform_c.cpp
IndexShards_c.cpp
IndexReplicas_c.cpp
Index_c.cpp
IndexBinary_c.cpp
IndexBinaryIVF_c.cpp
IndexBinaryFlat_c.cpp
IndexScalarQuantizer_c.cpp
MetaIndexes_c.cpp
clone_index_c.cpp
error_impl.cpp
index_factory_c.cpp
index_io_c.cpp
impl/AuxIndexStructures_c.cpp
impl/io_c.cpp
invlists/DirectMap_c.cpp
utils/distances_c.cpp
utils/utils_c.cpp
)
add_library(faiss_c ${FAISS_C_SRC})
target_link_libraries(faiss_c PRIVATE faiss)
add_library(faiss_c_avx2 ${FAISS_C_SRC})
target_link_libraries(faiss_c_avx2 PRIVATE faiss_avx2)
if(NOT FAISS_OPT_LEVEL STREQUAL "avx2" AND NOT FAISS_OPT_LEVEL STREQUAL "avx512" AND NOT FAISS_OPT_LEVEL STREQUAL "avx512_spr")
set_target_properties(faiss_c_avx2 PROPERTIES EXCLUDE_FROM_ALL TRUE)
endif()
if(NOT WIN32)
target_compile_options(faiss_c_avx2 PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-mavx2 -mfma -mf16c -mpopcnt>)
else()
# MSVC enables FMA with /arch:AVX2; no separate flags for F16C, POPCNT
# Ref. FMA (under /arch:AVX2): https://docs.microsoft.com/en-us/cpp/build/reference/arch-x64
# Ref. F16C (2nd paragraph): https://walbourn.github.io/directxmath-avx2/
# Ref. POPCNT: https://docs.microsoft.com/en-us/cpp/intrinsics/popcnt16-popcnt-popcnt64
target_compile_options(faiss_c_avx2 PRIVATE $<$<COMPILE_LANGUAGE:CXX>:/arch:AVX2>)
endif()
add_library(faiss_c_avx512 ${FAISS_C_SRC})
target_link_libraries(faiss_c_avx512 PRIVATE faiss_avx512)
if(NOT FAISS_OPT_LEVEL STREQUAL "avx512")
set_target_properties(faiss_c_avx512 PROPERTIES EXCLUDE_FROM_ALL TRUE)
endif()
if(NOT WIN32)
# All modern CPUs support F, CD, VL, DQ, BW extensions.
# Ref: https://en.wikipedia.org/wiki/AVX512
target_compile_options(faiss_c_avx512 PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-mavx2 -mfma -mf16c -mavx512f -mavx512cd -mavx512vl -mavx512dq -mavx512bw -mpopcnt>)
else()
target_compile_options(faiss_c_avx512 PRIVATE $<$<COMPILE_LANGUAGE:CXX>:/arch:AVX512>)
endif()
add_library(faiss_c_avx512_spr ${FAISS_C_SRC})
target_link_libraries(faiss_c_avx512_spr PRIVATE faiss_avx512_spr)
if(NOT FAISS_OPT_LEVEL STREQUAL "avx512_spr")
set_target_properties(faiss_c_avx512_spr PROPERTIES EXCLUDE_FROM_ALL TRUE)
endif()
if(NOT WIN32)
# Architecture mode to support AVX512 extensions available since Intel(R) Sapphire Rapids.
# Ref: https://networkbuilders.intel.com/solutionslibrary/intel-avx-512-fp16-instruction-set-for-intel-xeon-processor-based-products-technology-guide
target_compile_options(faiss_c_avx512_spr PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-mavx2 -mfma -mf16c -mavx512f -mavx512cd -mavx512vl -mavx512dq -mavx512bw -mavx512vpopcntdq -mavx512bitalg -mpopcnt -mavx512fp16 -mavx512bf16>)
else()
target_compile_options(faiss_c_avx512_spr PRIVATE $<$<COMPILE_LANGUAGE:CXX>:/arch:AVX512>)
endif()
add_library(faiss_c_sve ${FAISS_C_SRC})
target_link_libraries(faiss_c_sve PRIVATE faiss_sve)
if(NOT FAISS_OPT_LEVEL STREQUAL "sve")
set_target_properties(faiss_c_sve PROPERTIES EXCLUDE_FROM_ALL TRUE)
endif()
if(NOT WIN32)
if("${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_DEBUG} " MATCHES "(^| )-march=native")
# Do nothing, expect SVE to be enabled by -march=native
elseif("${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_DEBUG} " MATCHES "(^| )(-march=armv[0-9]+(\\.[1-9]+)?-[^+ ](\\+[^+$ ]+)*)")
# Add +sve
target_compile_options(faiss_c_sve PRIVATE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>:${CMAKE_MATCH_2}+sve>)
elseif(NOT "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_DEBUG} " MATCHES "(^| )-march=armv")
# No valid -march, so specify -march=armv8-a+sve as the default
target_compile_options(faiss_c_sve PRIVATE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:DEBUG>>:-march=armv8-a+sve>)
endif()
if("${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE} " MATCHES "(^| )-march=native")
# Do nothing, expect SVE to be enabled by -march=native
elseif("${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE} " MATCHES "(^| )(-march=armv[0-9]+(\\.[1-9]+)?-[^+ ](\\+[^+$ ]+)*)")
# Add +sve
target_compile_options(faiss_c_sve PRIVATE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>:${CMAKE_MATCH_2}+sve>)
elseif(NOT "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE} " MATCHES "(^| )-march=armv")
# No valid -march, so specify -march=armv8-a+sve as the default
target_compile_options(faiss_c_sve PRIVATE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CONFIG:RELEASE>>:-march=armv8-a+sve>)
endif()
endif()
function(faiss_install_headers headers p)
foreach(h ${headers})
get_filename_component(f ${h} DIRECTORY)
install(FILES ${h}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/faiss/${p}/${f}
)
endforeach()
endfunction()
file(GLOB FAISS_C_API_HEADERS
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
"*.h"
"impl/*.h"
"invlists/*.h"
"utils/*.h")
faiss_install_headers("${FAISS_C_API_HEADERS}" c_api)
install(TARGETS faiss_c
EXPORT faiss-targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
if(FAISS_OPT_LEVEL STREQUAL "avx2")
install(TARGETS faiss_c_avx2
EXPORT faiss-targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
endif()
if(FAISS_OPT_LEVEL STREQUAL "avx512")
install(TARGETS faiss_c_avx2 faiss_c_avx512
EXPORT faiss-targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
endif()
if(FAISS_OPT_LEVEL STREQUAL "avx512_spr")
install(TARGETS faiss_c_avx2 faiss_c_avx512_spr
EXPORT faiss-targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
endif()
if(FAISS_OPT_LEVEL STREQUAL "sve")
install(TARGETS faiss_c_sve
EXPORT faiss-targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
endif()
add_executable(example_c EXCLUDE_FROM_ALL example_c.c)
target_link_libraries(example_c PRIVATE faiss_c)
add_executable(example_search_params_ivf_c EXCLUDE_FROM_ALL
example_search_params_ivf_c.c)
target_link_libraries(example_search_params_ivf_c PRIVATE faiss_c)
if(FAISS_ENABLE_GPU)
add_subdirectory(gpu)
endif()