diff options
| author | Rafael CF Sousa <rafaelcfsousa@ibm.com> | 2022-05-23 16:20:47 -0300 |
|---|---|---|
| committer | Rafael CF Sousa <rafaelcfsousa@ibm.com> | 2022-05-23 18:53:43 -0300 |
| commit | 09b22a118466ff85fe365f451139cc3da2e8bc43 (patch) | |
| tree | 056ab9df3393f7f0be505bb656c80bc23f975529 /numpy | |
| parent | ff03e72646ee0e4727a1cc333d5ea4945724d44b (diff) | |
| download | numpy-09b22a118466ff85fe365f451139cc3da2e8bc43.tar.gz | |
SIMD, ENH: Use logical bitwise to implement comparison functions (bool_)
Diffstat (limited to 'numpy')
| -rw-r--r-- | numpy/core/src/_simd/_simd.dispatch.c.src | 6 | ||||
| -rw-r--r-- | numpy/core/src/common/simd/avx2/operators.h | 5 | ||||
| -rw-r--r-- | numpy/core/src/common/simd/avx512/operators.h | 12 | ||||
| -rw-r--r-- | numpy/core/src/common/simd/neon/operators.h | 5 | ||||
| -rw-r--r-- | numpy/core/src/common/simd/sse/operators.h | 5 | ||||
| -rw-r--r-- | numpy/core/src/common/simd/vsx/operators.h | 5 | ||||
| -rw-r--r-- | numpy/core/src/umath/loops_comparison.dispatch.c.src | 25 | ||||
| -rw-r--r-- | numpy/core/tests/test_simd.py | 45 |
8 files changed, 99 insertions, 9 deletions
diff --git a/numpy/core/src/_simd/_simd.dispatch.c.src b/numpy/core/src/_simd/_simd.dispatch.c.src index ad8c538e4..f8a0a3196 100644 --- a/numpy/core/src/_simd/_simd.dispatch.c.src +++ b/numpy/core/src/_simd/_simd.dispatch.c.src @@ -462,6 +462,9 @@ SIMD_IMPL_INTRIN_2(or_@bsfx@, v@bsfx@, v@bsfx@, v@bsfx@) SIMD_IMPL_INTRIN_2(xor_@bsfx@, v@bsfx@, v@bsfx@, v@bsfx@) SIMD_IMPL_INTRIN_1(not_@bsfx@, v@bsfx@, v@bsfx@) /**end repeat**/ +SIMD_IMPL_INTRIN_2(andc_b8, vb8, vb8, vb8) +SIMD_IMPL_INTRIN_2(orc_b8, vb8, vb8, vb8) +SIMD_IMPL_INTRIN_2(xnor_b8, vb8, vb8, vb8) /*************************** * Conversions ***************************/ @@ -710,6 +713,9 @@ SIMD_INTRIN_DEF(or_@bsfx@) SIMD_INTRIN_DEF(xor_@bsfx@) SIMD_INTRIN_DEF(not_@bsfx@) /**end repeat**/ +SIMD_INTRIN_DEF(andc_b8) +SIMD_INTRIN_DEF(orc_b8) +SIMD_INTRIN_DEF(xnor_b8) /*************************** * Conversions ***************************/ diff --git a/numpy/core/src/common/simd/avx2/operators.h b/numpy/core/src/common/simd/avx2/operators.h index 5fc7719e9..0e77fc6be 100644 --- a/numpy/core/src/common/simd/avx2/operators.h +++ b/numpy/core/src/common/simd/avx2/operators.h @@ -114,6 +114,11 @@ NPY_FINLINE __m256i npyv_shr_s64(__m256i a, int c) #define npyv_not_b32 npyv_not_u8 #define npyv_not_b64 npyv_not_u8 +// ANDC, ORC and XNOR +#define npyv_andc_b8(A, B) _mm256_andnot_si256(A, B) +#define npyv_orc_b8(A, B) npyv_or_b8(npyv_not_b8(A), B) +#define npyv_xnor_b8(A, B) npyv_not_b8(npyv_xor_b8(A, B)) + /*************************** * Comparison ***************************/ diff --git a/numpy/core/src/common/simd/avx512/operators.h b/numpy/core/src/common/simd/avx512/operators.h index d53932fa8..8c98b72dd 100644 --- a/numpy/core/src/common/simd/avx512/operators.h +++ b/numpy/core/src/common/simd/avx512/operators.h @@ -152,6 +152,9 @@ #define npyv_xor_b16 _kxor_mask32 #define npyv_not_b8 _knot_mask64 #define npyv_not_b16 _knot_mask32 + #define npyv_andc_b8 _kandn_mask64 + #define npyv_orc_b8(A, B) npyv_or_b8(npyv_not_b8(A), B) + #define npyv_xnor_b8 _kxnor_mask64 #elif defined(NPY_HAVE_AVX512BW) NPY_FINLINE npyv_b8 npyv_and_b8(npyv_b8 a, npyv_b8 b) { return a & b; } @@ -169,6 +172,12 @@ { return ~a; } NPY_FINLINE npyv_b16 npyv_not_b16(npyv_b16 a) { return ~a; } + NPY_FINLINE npyv_b8 npyv_andc_b8(npyv_b8 a, npyv_b8 b) + { return (~a) & b; } + NPY_FINLINE npyv_b8 npyv_orc_b8(npyv_b8 a, npyv_b8 b) + { return (~a) | b; } + NPY_FINLINE npyv_b8 npyv_xnor_b8(npyv_b8 a, npyv_b8 b) + { return ~(a ^ b); } #else #define npyv_and_b8 _mm512_and_si512 #define npyv_and_b16 _mm512_and_si512 @@ -178,6 +187,9 @@ #define npyv_xor_b16 _mm512_xor_si512 #define npyv_not_b8 npyv_not_u8 #define npyv_not_b16 npyv_not_u8 + #define npyv_andc_b8 _mm512_andnot_si512 + #define npyv_orc_b8(A, B) npyv_or_b8(npyv_not_b8(A), B) + #define npyv_xnor_b8(A, B) npyv_not_b8(npyv_xor_b8(A, B)) #endif #define npyv_and_b32 _mm512_kand diff --git a/numpy/core/src/common/simd/neon/operators.h b/numpy/core/src/common/simd/neon/operators.h index b43ba3653..6c155fc67 100644 --- a/numpy/core/src/common/simd/neon/operators.h +++ b/numpy/core/src/common/simd/neon/operators.h @@ -116,6 +116,11 @@ #define npyv_not_b32 vmvnq_u32 #define npyv_not_b64 npyv_not_u64 +// ANDC, ORC and XNOR +#define npyv_andc_b8(A, B) vbicq_u8(B, A) +#define npyv_orc_b8(A, B) vornq_u8(B, A) +#define npyv_xnor_b8 vceqq_u8 + /*************************** * Comparison ***************************/ diff --git a/numpy/core/src/common/simd/sse/operators.h b/numpy/core/src/common/simd/sse/operators.h index 51c84fb4e..51bdca356 100644 --- a/numpy/core/src/common/simd/sse/operators.h +++ b/numpy/core/src/common/simd/sse/operators.h @@ -115,6 +115,11 @@ NPY_FINLINE __m128i npyv_shr_s64(__m128i a, int c) #define npyv_not_b32 npyv_not_u8 #define npyv_not_b64 npyv_not_u8 +// ANDC, ORC and XNOR +#define npyv_andc_b8(A, B) _mm_andnot_si128(A, B) +#define npyv_orc_b8(A, B) npyv_or_b8(npyv_not_b8(A), B) +#define npyv_xnor_b8(A, B) npyv_not_b8(npyv_xor_b8(A, B)) + /*************************** * Comparison ***************************/ diff --git a/numpy/core/src/common/simd/vsx/operators.h b/numpy/core/src/common/simd/vsx/operators.h index d34057ff3..fc29ba920 100644 --- a/numpy/core/src/common/simd/vsx/operators.h +++ b/numpy/core/src/common/simd/vsx/operators.h @@ -133,6 +133,11 @@ NPY_FINLINE npyv_f32 npyv_not_f32(npyv_f32 a) NPY_FINLINE npyv_f64 npyv_not_f64(npyv_f64 a) { return vec_nor(a, a); } +// ANDC, ORC and XNOR +#define npyv_andc_b8(A, B) vec_andc(B, A) +#define npyv_orc_b8(A, B) vec_orc(B, A) +#define npyv_xnor_b8 vec_eqv + /*************************** * Comparison ***************************/ diff --git a/numpy/core/src/umath/loops_comparison.dispatch.c.src b/numpy/core/src/umath/loops_comparison.dispatch.c.src index e5518afb3..07bbf0354 100644 --- a/numpy/core/src/umath/loops_comparison.dispatch.c.src +++ b/numpy/core/src/umath/loops_comparison.dispatch.c.src @@ -207,7 +207,8 @@ static void simd_binary_scalar2_@kind@_@sfx@(char **args, npy_intp len) /**begin repeat * #kind = equal, not_equal, less, less_equal, greater, greater_equal# * #OP = ==, !=, <, <=, >, >=# - * #VOP = cmpeq, cmpneq, cmplt, cmple, cmpgt, cmpge# + * #VOP = xnor, xor, andc, orc, andc, orc# + * #rev = 0, 0, 0, 0, 1, 1# */ #if NPY_SIMD @@ -225,7 +226,11 @@ static void simd_binary_@kind@_b8(char **args, npy_intp len) // Whatever element in src != 0x0 is converted to 0xFF npyv_b8 a = npyv_cmpneq_u8(npyv_load_u8(src1), vzero); npyv_b8 b = npyv_cmpneq_u8(npyv_load_u8(src2), vzero); - npyv_b8 c = npyv_@VOP@_u8(npyv_cvt_u8_b8(a), npyv_cvt_u8_b8(b)); +#if !@rev@ + npyv_b8 c = npyv_@VOP@_b8(a, b); +#else + npyv_b8 c = npyv_@VOP@_b8(b, a); +#endif npyv_store_u8(dst, npyv_and_u8(npyv_cvt_u8_b8(c), truemask)); } @@ -243,13 +248,17 @@ static void simd_binary_scalar1_@kind@_b8(char **args, npy_intp len) npyv_lanetype_u8 *dst = (npyv_lanetype_u8 *) args[2]; const npyv_u8 vzero = npyv_setall_u8(0x0); const npyv_u8 vscalar = npyv_setall_u8(scalar); - const npyv_u8 a = npyv_cvt_u8_b8(npyv_cmpneq_u8(vscalar, vzero)); + const npyv_b8 a = npyv_cmpneq_u8(vscalar, vzero); const npyv_u8 truemask = npyv_setall_u8(0x1); const int vstep = npyv_nlanes_u8; for (; len >= vstep; len -= vstep, src += vstep, dst += vstep) { npyv_b8 b = npyv_cmpneq_u8(npyv_load_u8(src), vzero); - npyv_b8 c = npyv_@VOP@_u8(a, npyv_cvt_u8_b8(b)); +#if !@rev@ + npyv_b8 c = npyv_@VOP@_b8(a, b); +#else + npyv_b8 c = npyv_@VOP@_b8(b, a); +#endif npyv_store_u8(dst, npyv_and_u8(npyv_cvt_u8_b8(c), truemask)); } @@ -266,13 +275,17 @@ static void simd_binary_scalar2_@kind@_b8(char **args, npy_intp len) npyv_lanetype_u8 *dst = (npyv_lanetype_u8 *) args[2]; const npyv_u8 vzero = npyv_setall_u8(0x0); const npyv_u8 vscalar = npyv_setall_u8(scalar); - const npyv_u8 b = npyv_cvt_u8_b8(npyv_cmpneq_u8(vscalar, vzero)); + const npyv_b8 b = npyv_cmpneq_u8(vscalar, vzero); const npyv_u8 truemask = npyv_setall_u8(0x1); const int vstep = npyv_nlanes_u8; for (; len >= vstep; len -= vstep, src += vstep, dst += vstep) { npyv_b8 a = npyv_cmpneq_u8(npyv_load_u8(src), vzero); - npyv_b8 c = npyv_@VOP@_u8(npyv_cvt_u8_b8(a), b); +#if !@rev@ + npyv_b8 c = npyv_@VOP@_b8(a, b); +#else + npyv_b8 c = npyv_@VOP@_b8(b, a); +#endif npyv_store_u8(dst, npyv_and_u8(npyv_cvt_u8_b8(c), truemask)); } diff --git a/numpy/core/tests/test_simd.py b/numpy/core/tests/test_simd.py index b0b62b4a2..81d759ad4 100644 --- a/numpy/core/tests/test_simd.py +++ b/numpy/core/tests/test_simd.py @@ -156,6 +156,48 @@ class _SIMD_BOOL(_Test_Utility): tobits = bin(self.tobits(vdata)) assert tobits == bin(data_bits) + def test_andc(self): + if self.sfx not in ("b8"): + return + andc_simd = getattr(self.npyv, f"andc_b8") + # create the vectors + data = self._data() + rdata = self._data(reverse=True) + vdata = self._load_b(data) + vrdata = self._load_b(rdata) + # check andc + sandc = [(~x & y) & 0xFF for x, y in zip(rdata, data)] + vandc = andc_simd(vrdata, vdata) + assert sandc == vandc + + def test_orc(self): + if self.sfx not in ("b8"): + return + orc_simd = getattr(self.npyv, f"orc_b8") + # create the vectors + data = self._data() + rdata = self._data(reverse=True) + vdata = self._load_b(data) + vrdata = self._load_b(rdata) + # check orc + sorc = [(~x | y) & 0xFF for x, y in zip(rdata, data)] + vorc = orc_simd(vrdata, vdata) + assert sorc == vorc + + def test_xnor(self): + if self.sfx not in ("b8"): + return + xnor_simd = getattr(self.npyv, f"xnor_b8") + # create the vectors + data = self._data() + rdata = self._data(reverse=True) + vdata = self._load_b(data) + vrdata = self._load_b(rdata) + # check orc + sxnor = [~(x ^ y) & 0xFF for x, y in zip(rdata, data)] + vxnor = xnor_simd(vrdata, vdata) + assert sxnor == vxnor + def test_pack(self): """ Pack multiple vectors into one @@ -166,15 +208,12 @@ class _SIMD_BOOL(_Test_Utility): """ if self.sfx not in ("b16", "b32", "b64"): return - # create the vectors data = self._data() rdata = self._data(reverse=True) vdata = self._load_b(data) vrdata = self._load_b(rdata) - pack_simd = getattr(self.npyv, f"pack_b8_{self.sfx}") - # for scalar execution, concatenate the elements of the multiple lists # into a single list (spack) and then iterate over the elements of # the created list applying a mask to capture the first byte of them. |
