|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import platform |
|
|
import subprocess |
|
|
import logging |
|
|
import os |
|
|
|
|
|
|
|
|
def Version(v): |
|
|
return [int(x) for x in v.split('.')] |
|
|
|
|
|
def supported_instruction_sets(): |
|
|
""" |
|
|
Returns the set of supported CPU features, see |
|
|
https://github.com/numpy/numpy/blob/master/numpy/core/src/common/npy_cpu_features.h |
|
|
for the list of features that this set may contain per architecture. |
|
|
|
|
|
Example: |
|
|
>>> supported_instruction_sets() # for x86 |
|
|
{"SSE2", "AVX2", "AVX512", ...} |
|
|
>>> supported_instruction_sets() # for PPC |
|
|
{"VSX", "VSX2", ...} |
|
|
>>> supported_instruction_sets() # for ARM |
|
|
{"NEON", "ASIMD", ...} |
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
def is_sve_supported(): |
|
|
if platform.machine() != "aarch64": |
|
|
return False |
|
|
|
|
|
if platform.system() != "Linux": |
|
|
return False |
|
|
|
|
|
import numpy |
|
|
if Version(numpy.__version__) >= Version("2.0"): |
|
|
return False |
|
|
|
|
|
import numpy.distutils.cpuinfo |
|
|
return "sve" in numpy.distutils.cpuinfo.cpu.info[0].get('Features', "").split() |
|
|
|
|
|
import numpy |
|
|
if Version(numpy.__version__) >= Version("1.19"): |
|
|
|
|
|
from numpy.core._multiarray_umath import __cpu_features__ |
|
|
|
|
|
|
|
|
supported = {k for k, v in __cpu_features__.items() if v} |
|
|
if is_sve_supported(): |
|
|
supported.add("SVE") |
|
|
for f in os.getenv("FAISS_DISABLE_CPU_FEATURES", "").split(", \t\n\r"): |
|
|
supported.discard(f) |
|
|
return supported |
|
|
|
|
|
|
|
|
if platform.system() == "Darwin": |
|
|
if subprocess.check_output(["/usr/sbin/sysctl", "hw.optional.avx2_0"])[-1] == '1': |
|
|
return {"AVX2"} |
|
|
elif platform.system() == "Linux": |
|
|
import numpy.distutils.cpuinfo |
|
|
result = set() |
|
|
if "avx2" in numpy.distutils.cpuinfo.cpu.info[0].get('flags', ""): |
|
|
result.add("AVX2") |
|
|
if "avx512" in numpy.distutils.cpuinfo.cpu.info[0].get('flags', ""): |
|
|
result.add("AVX512") |
|
|
if "avx512_fp16" in numpy.distutils.cpuinfo.cpu.info[0].get('flags', ""): |
|
|
|
|
|
result.add("AVX512_SPR") |
|
|
if is_sve_supported(): |
|
|
result.add("SVE") |
|
|
for f in os.getenv("FAISS_DISABLE_CPU_FEATURES", "").split(", \t\n\r"): |
|
|
result.discard(f) |
|
|
return result |
|
|
return set() |
|
|
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
instruction_sets = None |
|
|
|
|
|
|
|
|
opt_env_variable_name = "FAISS_OPT_LEVEL" |
|
|
opt_level = os.environ.get(opt_env_variable_name, None) |
|
|
|
|
|
if opt_level is None: |
|
|
logger.debug(f"Environment variable {opt_env_variable_name} is not set, " \ |
|
|
"so let's pick the instruction set according to the current CPU") |
|
|
instruction_sets = supported_instruction_sets() |
|
|
else: |
|
|
logger.debug(f"Using {opt_level} as an instruction set.") |
|
|
instruction_sets = set() |
|
|
instruction_sets.add(opt_level) |
|
|
|
|
|
loaded = False |
|
|
has_AVX512_SPR = any("AVX512_SPR" in x.upper() for x in instruction_sets) |
|
|
if has_AVX512_SPR: |
|
|
try: |
|
|
logger.info("Loading faiss with AVX512-SPR support.") |
|
|
from .swigfaiss_avx512_spr import * |
|
|
logger.info("Successfully loaded faiss with AVX512-SPR support.") |
|
|
loaded = True |
|
|
except ImportError as e: |
|
|
logger.info(f"Could not load library with AVX512-SPR support due to:\n{e!r}") |
|
|
|
|
|
loaded = False |
|
|
|
|
|
has_AVX512 = any("AVX512" in x.upper() for x in instruction_sets) |
|
|
if has_AVX512 and not loaded: |
|
|
try: |
|
|
logger.info("Loading faiss with AVX512 support.") |
|
|
from .swigfaiss_avx512 import * |
|
|
logger.info("Successfully loaded faiss with AVX512 support.") |
|
|
loaded = True |
|
|
except ImportError as e: |
|
|
logger.info(f"Could not load library with AVX512 support due to:\n{e!r}") |
|
|
|
|
|
loaded = False |
|
|
|
|
|
has_AVX2 = "AVX2" in instruction_sets |
|
|
if has_AVX2 and not loaded: |
|
|
try: |
|
|
logger.info("Loading faiss with AVX2 support.") |
|
|
from .swigfaiss_avx2 import * |
|
|
logger.info("Successfully loaded faiss with AVX2 support.") |
|
|
loaded = True |
|
|
except ImportError as e: |
|
|
logger.info(f"Could not load library with AVX2 support due to:\n{e!r}") |
|
|
|
|
|
loaded = False |
|
|
|
|
|
has_SVE = "SVE" in instruction_sets |
|
|
if has_SVE and not loaded: |
|
|
try: |
|
|
logger.info("Loading faiss with SVE support.") |
|
|
from .swigfaiss_sve import * |
|
|
logger.info("Successfully loaded faiss with SVE support.") |
|
|
loaded = True |
|
|
except ImportError as e: |
|
|
logger.info(f"Could not load library with SVE support due to:\n{e!r}") |
|
|
|
|
|
loaded = False |
|
|
|
|
|
if not loaded: |
|
|
|
|
|
logger.info("Loading faiss.") |
|
|
from .swigfaiss import * |
|
|
logger.info("Successfully loaded faiss.") |
|
|
|