From 62519d8b18e9f535e5fa65169dad43d4e3bbc83b Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Wed, 30 Dec 2020 02:15:06 +0100 Subject: [PATCH 1/6] make AVX2-detection platform-independent --- faiss/python/loader.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/faiss/python/loader.py b/faiss/python/loader.py index ac01662305..561cf3f635 100644 --- a/faiss/python/loader.py +++ b/faiss/python/loader.py @@ -3,30 +3,39 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. +from distutils.version import LooseVersion import platform import subprocess import logging -def instruction_set(): +def supports_AVX2(): + import numpy + if LooseVersion(numpy.__version__) >= "1.19": + # use private API as next-best thing until numpy/numpy#18058 is solved + from numpy.core._multiarray_umath import __cpu_features__ + return __cpu_features__["AVX2"] + + # platform-dependent fallback before numpy 1.19, no windows if platform.system() == "Darwin": if subprocess.check_output(["/usr/sbin/sysctl", "hw.optional.avx2_0"])[-1] == '1': - return "AVX2" + return True else: - return "default" + return False elif platform.system() == "Linux": import numpy.distutils.cpuinfo if "avx2" in numpy.distutils.cpuinfo.cpu.info[0].get('flags', ""): - return "AVX2" + return True else: - return "default" + return False + return False logger = logging.getLogger(__name__) try: - instr_set = instruction_set() - if instr_set == "AVX2": + has_AVX2 = supports_AVX2() + if has_AVX2: logger.info("Loading faiss with AVX2 support.") from .swigfaiss_avx2 import * else: From 6d3f5a7f44cccd4bb8976275b87735a7279fbcd3 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Mon, 18 Jan 2021 12:52:35 +0100 Subject: [PATCH 2/6] review mdouze: keep instruction_set() --- faiss/python/loader.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/faiss/python/loader.py b/faiss/python/loader.py index 561cf3f635..6cd3b635eb 100644 --- a/faiss/python/loader.py +++ b/faiss/python/loader.py @@ -9,32 +9,45 @@ import logging -def supports_AVX2(): +def instruction_set(): + """ + Returns a dictionary for supported instruction sets, see + https://github.com/numpy/numpy/blob/master/numpy/core/src/common/npy_cpu_features.h + for the list of features that this dictionary contains per architecture. + + Example: + >>> instruction_set() # for x86 + {"SSE2": True, "AVX2": False, ...} + >>> instruction_set() # for PPC + {"VSX": True, "VSX2": False, ...} + >>> instruction_set() # for ARM + {"NEON": True, "ASIMD": False, ...} + """ import numpy if LooseVersion(numpy.__version__) >= "1.19": # use private API as next-best thing until numpy/numpy#18058 is solved from numpy.core._multiarray_umath import __cpu_features__ - return __cpu_features__["AVX2"] + return __cpu_features__ - # platform-dependent fallback before numpy 1.19, no windows + # platform-dependent legacy fallback before numpy 1.19, no windows if platform.system() == "Darwin": if subprocess.check_output(["/usr/sbin/sysctl", "hw.optional.avx2_0"])[-1] == '1': - return True + return {"AVX2": True} else: - return False + return {"AVX2": False} elif platform.system() == "Linux": import numpy.distutils.cpuinfo if "avx2" in numpy.distutils.cpuinfo.cpu.info[0].get('flags', ""): - return True + return {"AVX2": True} else: - return False - return False + return {"AVX2": False} + return {"AVX2": False} logger = logging.getLogger(__name__) try: - has_AVX2 = supports_AVX2() + has_AVX2 = instruction_set()["AVX2"] # dict-values of instruction_set() are True or False if has_AVX2: logger.info("Loading faiss with AVX2 support.") from .swigfaiss_avx2 import * From f2df3a47d671f153589e47f6e8b19ca7483855d8 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Thu, 21 Jan 2021 12:45:32 +0100 Subject: [PATCH 3/6] remove redundant return statements --- faiss/python/loader.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/faiss/python/loader.py b/faiss/python/loader.py index 6cd3b635eb..469d6f527d 100644 --- a/faiss/python/loader.py +++ b/faiss/python/loader.py @@ -33,14 +33,10 @@ def instruction_set(): if platform.system() == "Darwin": if subprocess.check_output(["/usr/sbin/sysctl", "hw.optional.avx2_0"])[-1] == '1': return {"AVX2": True} - else: - return {"AVX2": False} elif platform.system() == "Linux": import numpy.distutils.cpuinfo if "avx2" in numpy.distutils.cpuinfo.cpu.info[0].get('flags', ""): return {"AVX2": True} - else: - return {"AVX2": False} return {"AVX2": False} From 95d60df4dd78a56c1889c2f89b5854bdf8e14844 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Fri, 29 Jan 2021 12:00:27 +0100 Subject: [PATCH 4/6] avoid KeyError (review mdouze) --- faiss/python/loader.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/faiss/python/loader.py b/faiss/python/loader.py index 469d6f527d..e591575fab 100644 --- a/faiss/python/loader.py +++ b/faiss/python/loader.py @@ -43,7 +43,10 @@ def instruction_set(): logger = logging.getLogger(__name__) try: - has_AVX2 = instruction_set()["AVX2"] # dict-values of instruction_set() are True or False + instr_set = instruction_set() + # dict-values of instr_set are True or False, but do not have + # uniform keys across arches -> use fallback value of False + has_AVX2 = instr_set.get("AVX2", False) if has_AVX2: logger.info("Loading faiss with AVX2 support.") from .swigfaiss_avx2 import * From 0d6863bfbb28ca5ec8e9be4cb44adb08d9ff5f01 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Fri, 29 Jan 2021 16:08:14 +0100 Subject: [PATCH 5/6] turn instruction_set into set (review beauby) --- faiss/python/loader.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/faiss/python/loader.py b/faiss/python/loader.py index e591575fab..70db15133e 100644 --- a/faiss/python/loader.py +++ b/faiss/python/loader.py @@ -11,42 +11,42 @@ def instruction_set(): """ - Returns a dictionary for supported instruction sets, see + 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 dictionary contains per architecture. + for the list of features that this set may contain per architecture. Example: >>> instruction_set() # for x86 - {"SSE2": True, "AVX2": False, ...} + {"SSE2", "AVX2", ...} >>> instruction_set() # for PPC - {"VSX": True, "VSX2": False, ...} + {"VSX", "VSX2", ...} >>> instruction_set() # for ARM - {"NEON": True, "ASIMD": False, ...} + {"NEON", "ASIMD", ...} """ import numpy if LooseVersion(numpy.__version__) >= "1.19": # use private API as next-best thing until numpy/numpy#18058 is solved from numpy.core._multiarray_umath import __cpu_features__ - return __cpu_features__ + # __cpu_features__ is a dictionary with CPU features + # as keys, and True / False as values + supported = {k for k, v in __cpu_features__.items() if v} + return supported # platform-dependent legacy fallback before numpy 1.19, no windows if platform.system() == "Darwin": if subprocess.check_output(["/usr/sbin/sysctl", "hw.optional.avx2_0"])[-1] == '1': - return {"AVX2": True} + return {"AVX2"} elif platform.system() == "Linux": import numpy.distutils.cpuinfo if "avx2" in numpy.distutils.cpuinfo.cpu.info[0].get('flags', ""): - return {"AVX2": True} - return {"AVX2": False} + return {"AVX2"} + return set() logger = logging.getLogger(__name__) try: - instr_set = instruction_set() - # dict-values of instr_set are True or False, but do not have - # uniform keys across arches -> use fallback value of False - has_AVX2 = instr_set.get("AVX2", False) + has_AVX2 = "AVX2" in instruction_set() if has_AVX2: logger.info("Loading faiss with AVX2 support.") from .swigfaiss_avx2 import * From 4bfa7bfaa4c9b1d67c2d1daa83dffa6fbeafd646 Mon Sep 17 00:00:00 2001 From: "H. Vetinari" Date: Fri, 29 Jan 2021 16:11:50 +0100 Subject: [PATCH 6/6] rename instruction_set to something less ambiguous It is a set of instruction sets, and "instruction_set" is not great for that --- faiss/python/loader.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/faiss/python/loader.py b/faiss/python/loader.py index 70db15133e..49601edffa 100644 --- a/faiss/python/loader.py +++ b/faiss/python/loader.py @@ -9,18 +9,18 @@ import logging -def instruction_set(): +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: - >>> instruction_set() # for x86 + >>> supported_instruction_sets() # for x86 {"SSE2", "AVX2", ...} - >>> instruction_set() # for PPC + >>> supported_instruction_sets() # for PPC {"VSX", "VSX2", ...} - >>> instruction_set() # for ARM + >>> supported_instruction_sets() # for ARM {"NEON", "ASIMD", ...} """ import numpy @@ -46,7 +46,7 @@ def instruction_set(): logger = logging.getLogger(__name__) try: - has_AVX2 = "AVX2" in instruction_set() + has_AVX2 = "AVX2" in supported_instruction_sets() if has_AVX2: logger.info("Loading faiss with AVX2 support.") from .swigfaiss_avx2 import *