Skip to content

Commit eefa391

Browse files
Introduce avx512 optimization mode and FAISS_OPT_LEVEL env variable (facebookresearch#3150)
Summary: Enables avx512 optimized code (AVX512 subsets F, CD, VL, DQ and BW, which are available for Intel Skylake+ and all AMD Zen4). Also, introduces `FAISS_OPT_LEVEL` environment variable. Set it to `AVX2`, `AVX512` or empty to pick the appropriate x86_64 instruction set. Compiled via the following ``` cmake -B build -DCMAKE_BUILD_TYPE=Release -DFAISS_ENABLE_GPU=OFF -DFAISS_OPT_LEVEL=avx512 -DBUILD_TESTING=ON . make -C build -j 8 faiss_test make -C build -j 8 swigfaiss make -C build -j 8 swigfaiss_avx2 make -C build -j 8 swigfaiss_avx512 cd build/faiss/python python3 setup.py build python3 setup.py install --force ``` Now, running the following script `1.py` ``` import logging logging.basicConfig(level=logging.DEBUG) import faiss ``` produces the following: ``` root@6179abeef23c:~/faiss# LOGLEVEL=DEBUG FAISS_OPT_LEVEL= python3 1.py DEBUG:faiss.loader:Using as an instruction set. INFO:faiss.loader:Loading faiss. INFO:faiss.loader:Successfully loaded faiss. root@6179abeef23c:~/faiss# LOGLEVEL=DEBUG FAISS_OPT_LEVEL=AVX2 python3 1.py DEBUG:faiss.loader:Using AVX2 as an instruction set. INFO:faiss.loader:Loading faiss with AVX2 support. INFO:faiss.loader:Successfully loaded faiss with AVX2 support. root@6179abeef23c:~/faiss# LOGLEVEL=DEBUG FAISS_OPT_LEVEL=AVX512 python3 1.py DEBUG:faiss.loader:Using AVX512 as an instruction set. INFO:faiss.loader:Loading faiss with AVX512 support. INFO:faiss.loader:Successfully loaded faiss with AVX512 support. root@6179abeef23c:~/faiss# LOGLEVEL=DEBUG python3 1.py DEBUG:faiss.loader:Environment variable FAISS_OPT_LEVEL is not set, so let's pick the instruction set according to the current CPU INFO:faiss.loader:Loading faiss with AVX512 support. INFO:faiss.loader:Successfully loaded faiss with AVX512 support. ``` Pull Request resolved: facebookresearch#3150 Reviewed By: algoriddle Differential Revision: D51701077 Pulled By: mdouze fbshipit-source-id: 4db05a287e763ff1ce1f676df7f7402532bf1e9e
1 parent 4c83965 commit eefa391

11 files changed

+131
-16
lines changed

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ set(CMAKE_CXX_STANDARD 17)
5050

5151
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
5252

53-
# Valid values are "generic", "avx2".
53+
# Valid values are "generic", "avx2", "avx512".
5454
option(FAISS_OPT_LEVEL "" "generic")
5555
option(FAISS_ENABLE_GPU "Enable support for GPU indexes." ON)
5656
option(FAISS_ENABLE_RAFT "Enable RAFT for GPU indexes." OFF)

INSTALL.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,8 @@ Several options can be passed to CMake, among which:
119119
- `-DCMAKE_BUILD_TYPE=Release` in order to enable generic compiler
120120
optimization options (enables `-O3` on gcc for instance),
121121
- `-DFAISS_OPT_LEVEL=avx2` in order to enable the required compiler flags to
122-
generate code using optimized SIMD instructions (possible values are `generic`
123-
and `avx2`, by increasing order of optimization),
122+
generate code using optimized SIMD instructions (possible values are `generic`,
123+
`avx2` and `avx512`, by increasing order of optimization),
124124
- BLAS-related options:
125125
- `-DBLA_VENDOR=Intel10_64_dyn -DMKL_LIBRARIES=/path/to/mkl/libs` to use the
126126
Intel MKL BLAS implementation, which is significantly faster than OpenBLAS

faiss/CMakeLists.txt

+34
Original file line numberDiff line numberDiff line change
@@ -244,12 +244,29 @@ else()
244244
add_compile_options(/bigobj)
245245
endif()
246246

247+
add_library(faiss_avx512 ${FAISS_SRC})
248+
if(NOT FAISS_OPT_LEVEL STREQUAL "avx512")
249+
set_target_properties(faiss_avx512 PROPERTIES EXCLUDE_FROM_ALL TRUE)
250+
endif()
251+
if(NOT WIN32)
252+
# All modern CPUs support F, CD, VL, DQ, BW extensions.
253+
# Ref: https://en.wikipedia.org/wiki/AVX512
254+
target_compile_options(faiss_avx512 PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-mavx2 -mfma -mf16c -mavx512f -mavx512cd -mavx512vl -mavx512dq -mavx512bw -mpopcnt>)
255+
else()
256+
target_compile_options(faiss_avx512 PRIVATE $<$<COMPILE_LANGUAGE:CXX>:/arch:AVX512>)
257+
# we need bigobj for the swig wrapper
258+
add_compile_options(/bigobj)
259+
endif()
260+
247261
# Handle `#include <faiss/foo.h>`.
248262
target_include_directories(faiss PUBLIC
249263
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>)
250264
# Handle `#include <faiss/foo.h>`.
251265
target_include_directories(faiss_avx2 PUBLIC
252266
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>)
267+
# Handle `#include <faiss/foo.h>`.
268+
target_include_directories(faiss_avx512 PUBLIC
269+
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>)
253270

254271
set_target_properties(faiss PROPERTIES
255272
POSITION_INDEPENDENT_CODE ON
@@ -259,31 +276,41 @@ set_target_properties(faiss_avx2 PROPERTIES
259276
POSITION_INDEPENDENT_CODE ON
260277
WINDOWS_EXPORT_ALL_SYMBOLS ON
261278
)
279+
set_target_properties(faiss_avx512 PROPERTIES
280+
POSITION_INDEPENDENT_CODE ON
281+
WINDOWS_EXPORT_ALL_SYMBOLS ON
282+
)
262283

263284
if(WIN32)
264285
target_compile_definitions(faiss PRIVATE FAISS_MAIN_LIB)
265286
target_compile_definitions(faiss_avx2 PRIVATE FAISS_MAIN_LIB)
287+
target_compile_definitions(faiss_avx512 PRIVATE FAISS_MAIN_LIB)
266288
endif()
267289

268290
target_compile_definitions(faiss PRIVATE FINTEGER=int)
269291
target_compile_definitions(faiss_avx2 PRIVATE FINTEGER=int)
292+
target_compile_definitions(faiss_avx512 PRIVATE FINTEGER=int)
270293

271294
find_package(OpenMP REQUIRED)
272295
target_link_libraries(faiss PRIVATE OpenMP::OpenMP_CXX)
273296
target_link_libraries(faiss_avx2 PRIVATE OpenMP::OpenMP_CXX)
297+
target_link_libraries(faiss_avx512 PRIVATE OpenMP::OpenMP_CXX)
274298

275299
find_package(MKL)
276300
if(MKL_FOUND)
277301
target_link_libraries(faiss PRIVATE ${MKL_LIBRARIES})
278302
target_link_libraries(faiss_avx2 PRIVATE ${MKL_LIBRARIES})
303+
target_link_libraries(faiss_avx512 PRIVATE ${MKL_LIBRARIES})
279304
else()
280305
find_package(BLAS REQUIRED)
281306
target_link_libraries(faiss PRIVATE ${BLAS_LIBRARIES})
282307
target_link_libraries(faiss_avx2 PRIVATE ${BLAS_LIBRARIES})
308+
target_link_libraries(faiss_avx512 PRIVATE ${BLAS_LIBRARIES})
283309

284310
find_package(LAPACK REQUIRED)
285311
target_link_libraries(faiss PRIVATE ${LAPACK_LIBRARIES})
286312
target_link_libraries(faiss_avx2 PRIVATE ${LAPACK_LIBRARIES})
313+
target_link_libraries(faiss_avx512 PRIVATE ${LAPACK_LIBRARIES})
287314
endif()
288315

289316
install(TARGETS faiss
@@ -300,6 +327,13 @@ if(FAISS_OPT_LEVEL STREQUAL "avx2")
300327
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
301328
)
302329
endif()
330+
if(FAISS_OPT_LEVEL STREQUAL "avx512")
331+
install(TARGETS faiss_avx512
332+
EXPORT faiss-targets
333+
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
334+
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
335+
)
336+
endif()
303337

304338
foreach(header ${FAISS_HEADERS})
305339
get_filename_component(dir ${header} DIRECTORY )

faiss/python/CMakeLists.txt

+28
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,24 @@ endmacro()
4444
# CMake's SWIG wrappers only allow tweaking certain settings at source level, so
4545
# we duplicate the source in order to override the module name.
4646
configure_file(swigfaiss.swig ${CMAKE_CURRENT_SOURCE_DIR}/swigfaiss_avx2.swig COPYONLY)
47+
configure_file(swigfaiss.swig ${CMAKE_CURRENT_SOURCE_DIR}/swigfaiss_avx512.swig COPYONLY)
4748

4849
configure_swigfaiss(swigfaiss.swig)
4950
configure_swigfaiss(swigfaiss_avx2.swig)
51+
configure_swigfaiss(swigfaiss_avx512.swig)
5052

5153
if(TARGET faiss)
5254
# Manually add headers as extra dependencies of swigfaiss.
5355
set(SWIG_MODULE_swigfaiss_EXTRA_DEPS)
5456
foreach(h ${FAISS_HEADERS})
5557
list(APPEND SWIG_MODULE_swigfaiss_EXTRA_DEPS "${faiss_SOURCE_DIR}/faiss/${h}")
5658
list(APPEND SWIG_MODULE_swigfaiss_avx2_EXTRA_DEPS "${faiss_SOURCE_DIR}/faiss/${h}")
59+
list(APPEND SWIG_MODULE_swigfaiss_avx512_EXTRA_DEPS "${faiss_SOURCE_DIR}/faiss/${h}")
5760
endforeach()
5861
foreach(h ${FAISS_GPU_HEADERS})
5962
list(APPEND SWIG_MODULE_swigfaiss_EXTRA_DEPS "${faiss_SOURCE_DIR}/faiss/gpu/${h}")
6063
list(APPEND SWIG_MODULE_swigfaiss_avx2_EXTRA_DEPS "${faiss_SOURCE_DIR}/faiss/gpu/${h}")
64+
list(APPEND SWIG_MODULE_swigfaiss_avx512_EXTRA_DEPS "${faiss_SOURCE_DIR}/faiss/gpu/${h}")
6165
endforeach()
6266
else()
6367
find_package(faiss REQUIRED)
@@ -82,14 +86,28 @@ if(NOT FAISS_OPT_LEVEL STREQUAL "avx2")
8286
set_target_properties(swigfaiss_avx2 PROPERTIES EXCLUDE_FROM_ALL TRUE)
8387
endif()
8488

89+
set_property(SOURCE swigfaiss_avx512.swig
90+
PROPERTY SWIG_MODULE_NAME swigfaiss_avx512)
91+
swig_add_library(swigfaiss_avx512
92+
TYPE SHARED
93+
LANGUAGE python
94+
SOURCES swigfaiss_avx512.swig
95+
)
96+
set_property(TARGET swigfaiss_avx512 PROPERTY SWIG_COMPILE_OPTIONS -doxygen)
97+
if(NOT FAISS_OPT_LEVEL STREQUAL "avx512")
98+
set_target_properties(swigfaiss_avx512 PROPERTIES EXCLUDE_FROM_ALL TRUE)
99+
endif()
100+
85101
if(NOT WIN32)
86102
# NOTE: Python does not recognize the dylib extension.
87103
set_target_properties(swigfaiss PROPERTIES SUFFIX .so)
88104
set_target_properties(swigfaiss_avx2 PROPERTIES SUFFIX .so)
105+
set_target_properties(swigfaiss_avx512 PROPERTIES SUFFIX .so)
89106
else()
90107
# we need bigobj for the swig wrapper
91108
target_compile_options(swigfaiss PRIVATE /bigobj)
92109
target_compile_options(swigfaiss_avx2 PRIVATE /bigobj)
110+
target_compile_options(swigfaiss_avx512 PRIVATE /bigobj)
93111
endif()
94112

95113
if(FAISS_ENABLE_GPU)
@@ -99,6 +117,7 @@ if(FAISS_ENABLE_GPU)
99117
endif()
100118
target_link_libraries(swigfaiss PRIVATE CUDA::cudart $<$<BOOL:${FAISS_ENABLE_RAFT}>:raft::raft> $<$<BOOL:${FAISS_ENABLE_RAFT}>:nvidia::cutlass::cutlass>)
101119
target_link_libraries(swigfaiss_avx2 PRIVATE CUDA::cudart $<$<BOOL:${FAISS_ENABLE_RAFT}>:raft::raft> $<$<BOOL:${FAISS_ENABLE_RAFT}>:nvidia::cutlass::cutlass>)
120+
target_link_libraries(swigfaiss_avx512 PRIVATE CUDA::cudart $<$<BOOL:${FAISS_ENABLE_RAFT}>:raft::raft> $<$<BOOL:${FAISS_ENABLE_RAFT}>:nvidia::cutlass::cutlass>)
102121
endif()
103122

104123
find_package(OpenMP REQUIRED)
@@ -117,10 +136,18 @@ target_link_libraries(swigfaiss_avx2 PRIVATE
117136
OpenMP::OpenMP_CXX
118137
)
119138

139+
target_link_libraries(swigfaiss_avx512 PRIVATE
140+
faiss_avx512
141+
Python::Module
142+
Python::NumPy
143+
OpenMP::OpenMP_CXX
144+
)
145+
120146
# Hack so that python_callbacks.h can be included as
121147
# `#include <faiss/python/python_callbacks.h>`.
122148
target_include_directories(swigfaiss PRIVATE ${PROJECT_SOURCE_DIR}/../..)
123149
target_include_directories(swigfaiss_avx2 PRIVATE ${PROJECT_SOURCE_DIR}/../..)
150+
target_include_directories(swigfaiss_avx512 PRIVATE ${PROJECT_SOURCE_DIR}/../..)
124151

125152
find_package(Python REQUIRED
126153
COMPONENTS Development NumPy
@@ -140,6 +167,7 @@ target_include_directories(faiss_python_callbacks PRIVATE ${Python_INCLUDE_DIRS}
140167

141168
target_link_libraries(swigfaiss PRIVATE faiss_python_callbacks)
142169
target_link_libraries(swigfaiss_avx2 PRIVATE faiss_python_callbacks)
170+
target_link_libraries(swigfaiss_avx512 PRIVATE faiss_python_callbacks)
143171

144172
configure_file(setup.py setup.py COPYONLY)
145173
configure_file(__init__.py __init__.py COPYONLY)

faiss/python/loader.py

+39-6
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def supported_instruction_sets():
1818
1919
Example:
2020
>>> supported_instruction_sets() # for x86
21-
{"SSE2", "AVX2", ...}
21+
{"SSE2", "AVX2", "AVX512", ...}
2222
>>> supported_instruction_sets() # for PPC
2323
{"VSX", "VSX2", ...}
2424
>>> supported_instruction_sets() # for ARM
@@ -41,25 +41,58 @@ def supported_instruction_sets():
4141
return {"AVX2"}
4242
elif platform.system() == "Linux":
4343
import numpy.distutils.cpuinfo
44+
result = set()
4445
if "avx2" in numpy.distutils.cpuinfo.cpu.info[0].get('flags', ""):
45-
return {"AVX2"}
46+
result.add("AVX2")
47+
if "avx512" in numpy.distutils.cpuinfo.cpu.info[0].get('flags', ""):
48+
result.add("AVX512")
49+
return result
4650
return set()
4751

4852

4953
logger = logging.getLogger(__name__)
5054

51-
has_AVX2 = "AVX2" in supported_instruction_sets()
52-
if has_AVX2:
55+
instruction_sets = None
56+
57+
# try to load optimization level from env variable
58+
opt_env_variable_name = "FAISS_OPT_LEVEL"
59+
opt_level = os.environ.get(opt_env_variable_name, None)
60+
61+
if opt_level is None:
62+
logger.debug(f"Environment variable {opt_env_variable_name} is not set, " \
63+
"so let's pick the instruction set according to the current CPU")
64+
instruction_sets = supported_instruction_sets()
65+
else:
66+
logger.debug(f"Using {opt_level} as an instruction set.")
67+
instruction_sets = set()
68+
instruction_sets.add(opt_level)
69+
70+
loaded = False
71+
has_AVX512 = any("AVX512" in x.upper() for x in instruction_sets)
72+
if has_AVX512:
73+
try:
74+
logger.info("Loading faiss with AVX512 support.")
75+
from .swigfaiss_avx512 import *
76+
logger.info("Successfully loaded faiss with AVX512 support.")
77+
loaded = True
78+
except ImportError as e:
79+
logger.info(f"Could not load library with AVX512 support due to:\n{e!r}")
80+
# reset so that we load without AVX512 below
81+
loaded = False
82+
83+
has_AVX2 = "AVX2" in instruction_sets
84+
if has_AVX2 and not loaded:
5385
try:
5486
logger.info("Loading faiss with AVX2 support.")
5587
from .swigfaiss_avx2 import *
5688
logger.info("Successfully loaded faiss with AVX2 support.")
89+
loaded = True
5790
except ImportError as e:
5891
logger.info(f"Could not load library with AVX2 support due to:\n{e!r}")
5992
# reset so that we load without AVX2 below
60-
has_AVX2 = False
93+
loaded = False
6194

62-
if not has_AVX2:
95+
if not loaded:
6396
# we import * so that the symbol X can be accessed as faiss.X
6497
logger.info("Loading faiss.")
6598
from .swigfaiss import *

faiss/python/setup.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@
2525

2626
swigfaiss_generic_lib = f"{prefix}_swigfaiss{ext}"
2727
swigfaiss_avx2_lib = f"{prefix}_swigfaiss_avx2{ext}"
28+
swigfaiss_avx512_lib = f"{prefix}_swigfaiss_avx512{ext}"
2829

2930
found_swigfaiss_generic = os.path.exists(swigfaiss_generic_lib)
3031
found_swigfaiss_avx2 = os.path.exists(swigfaiss_avx2_lib)
32+
found_swigfaiss_avx512 = os.path.exists(swigfaiss_avx512_lib)
3133

32-
assert (found_swigfaiss_generic or found_swigfaiss_avx2), \
34+
assert (found_swigfaiss_generic or found_swigfaiss_avx2 or found_swigfaiss_avx512), \
3335
f"Could not find {swigfaiss_generic_lib} or " \
34-
f"{swigfaiss_avx2_lib}. Faiss may not be compiled yet."
36+
f"{swigfaiss_avx2_lib} or {swigfaiss_avx512_lib}. Faiss may not be compiled yet."
3537

3638
if found_swigfaiss_generic:
3739
print(f"Copying {swigfaiss_generic_lib}")
@@ -43,6 +45,11 @@
4345
shutil.copyfile("swigfaiss_avx2.py", "faiss/swigfaiss_avx2.py")
4446
shutil.copyfile(swigfaiss_avx2_lib, f"faiss/_swigfaiss_avx2{ext}")
4547

48+
if found_swigfaiss_avx512:
49+
print(f"Copying {swigfaiss_avx512_lib}")
50+
shutil.copyfile("swigfaiss_avx512.py", "faiss/swigfaiss_avx512.py")
51+
shutil.copyfile(swigfaiss_avx512_lib, f"faiss/_swigfaiss_avx512{ext}")
52+
4653
long_description="""
4754
Faiss is a library for efficient similarity search and clustering of dense
4855
vectors. It contains algorithms that search in sets of vectors of any size,

faiss/utils/distances_fused/avx512.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
#include <faiss/utils/distances_fused/avx512.h>
1111

12-
#ifdef __AVX512__
12+
#ifdef __AVX512F__
1313

1414
#include <immintrin.h>
1515

faiss/utils/distances_fused/avx512.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
#include <faiss/utils/Heap.h>
1818

19-
#ifdef __AVX512__
19+
#ifdef __AVX512F__
2020

2121
namespace faiss {
2222

faiss/utils/distances_fused/distances_fused.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ bool exhaustive_L2sqr_fused_cmax(
2727
return true;
2828
}
2929

30-
#ifdef __AVX512__
30+
#ifdef __AVX512F__
3131
// avx512 kernel
3232
return exhaustive_L2sqr_fused_cmax_AVX512(x, y, d, nx, ny, res, y_norms);
3333
#elif defined(__AVX2__) || defined(__aarch64__)

faiss/utils/utils.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ std::string get_compile_options() {
116116

117117
#ifdef __AVX2__
118118
options += "AVX2 ";
119+
#elif __AVX512F__
120+
options += "AVX512";
119121
#elif defined(__aarch64__)
120122
options += "NEON ";
121123
#else

tests/CMakeLists.txt

+13-2
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,26 @@ set(FAISS_TEST_SRC
3434

3535
add_executable(faiss_test ${FAISS_TEST_SRC})
3636

37+
if(NOT FAISS_OPT_LEVEL STREQUAL "avx2" AND NOT FAISS_OPT_LEVEL STREQUAL "avx512")
38+
target_link_libraries(faiss_test PRIVATE faiss)
39+
endif()
40+
3741
if(FAISS_OPT_LEVEL STREQUAL "avx2")
3842
if(NOT WIN32)
3943
target_compile_options(faiss_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-mavx2 -mfma>)
4044
else()
4145
target_compile_options(faiss_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:/arch:AVX2>)
4246
endif()
4347
target_link_libraries(faiss_test PRIVATE faiss_avx2)
44-
else()
45-
target_link_libraries(faiss_test PRIVATE faiss)
48+
endif()
49+
50+
if(FAISS_OPT_LEVEL STREQUAL "avx512")
51+
if(NOT WIN32)
52+
target_compile_options(faiss_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-mavx2 -mfma -mavx512f -mavx512f -mavx512cd -mavx512vl -mavx512dq -mavx512bw>)
53+
else()
54+
target_compile_options(faiss_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:/arch:AVX512>)
55+
endif()
56+
target_link_libraries(faiss_test PRIVATE faiss_avx512)
4657
endif()
4758

4859
include(FetchContent)

0 commit comments

Comments
 (0)