usearch 2.16.8 → 2.16.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/include/usearch/index.hpp +1 -1
- package/package.json +1 -1
- package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
- package/prebuilds/linux-arm64/usearch.node +0 -0
- package/prebuilds/linux-x64/usearch.node +0 -0
- package/simsimd/CONTRIBUTING.md +69 -12
- package/simsimd/Cargo.lock +1 -1
- package/simsimd/Cargo.toml +1 -1
- package/simsimd/README.md +253 -40
- package/simsimd/c/lib.c +114 -109
- package/simsimd/include/simsimd/binary.h +33 -17
- package/simsimd/include/simsimd/curved.h +867 -129
- package/simsimd/include/simsimd/dot.h +562 -569
- package/simsimd/include/simsimd/probability.h +14 -9
- package/simsimd/include/simsimd/simsimd.h +102 -46
- package/simsimd/include/simsimd/spatial.h +28 -18
- package/simsimd/include/simsimd/types.h +24 -0
- package/simsimd/javascript/fallback.ts +26 -15
- package/simsimd/javascript/simsimd.ts +1 -1
- package/simsimd/package-lock.json +10 -9
- package/simsimd/package.json +2 -2
- package/simsimd/rust/lib.rs +218 -5
- package/simsimd/scripts/bench.cxx +156 -97
- package/simsimd/scripts/test.c +14 -8
- package/simsimd/scripts/test.mjs +158 -145
- package/simsimd/scripts/test.py +71 -52
|
@@ -39,20 +39,20 @@ extern "C" {
|
|
|
39
39
|
* For double-precision computation check out the "*_accurate" variants of those "*_serial" functions.
|
|
40
40
|
*/
|
|
41
41
|
SIMSIMD_PUBLIC void simsimd_dot_f64_serial(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
42
|
-
SIMSIMD_PUBLIC void simsimd_dot_f64c_serial(
|
|
43
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f64c_serial(
|
|
42
|
+
SIMSIMD_PUBLIC void simsimd_dot_f64c_serial(simsimd_f64c_t const* a, simsimd_f64c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
43
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f64c_serial(simsimd_f64c_t const* a, simsimd_f64c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
44
44
|
|
|
45
45
|
SIMSIMD_PUBLIC void simsimd_dot_f32_serial(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
46
|
-
SIMSIMD_PUBLIC void simsimd_dot_f32c_serial(
|
|
47
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f32c_serial(
|
|
46
|
+
SIMSIMD_PUBLIC void simsimd_dot_f32c_serial(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
47
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f32c_serial(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
48
48
|
|
|
49
49
|
SIMSIMD_PUBLIC void simsimd_dot_f16_serial(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
50
|
-
SIMSIMD_PUBLIC void simsimd_dot_f16c_serial(
|
|
51
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f16c_serial(
|
|
50
|
+
SIMSIMD_PUBLIC void simsimd_dot_f16c_serial(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
51
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f16c_serial(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
52
52
|
|
|
53
53
|
SIMSIMD_PUBLIC void simsimd_dot_bf16_serial(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
54
|
-
SIMSIMD_PUBLIC void simsimd_dot_bf16c_serial(
|
|
55
|
-
SIMSIMD_PUBLIC void simsimd_vdot_bf16c_serial(
|
|
54
|
+
SIMSIMD_PUBLIC void simsimd_dot_bf16c_serial(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
55
|
+
SIMSIMD_PUBLIC void simsimd_vdot_bf16c_serial(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
56
56
|
|
|
57
57
|
SIMSIMD_PUBLIC void simsimd_dot_i8_serial(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
58
58
|
SIMSIMD_PUBLIC void simsimd_dot_u8_serial(simsimd_u8_t const* a, simsimd_u8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
@@ -61,50 +61,50 @@ SIMSIMD_PUBLIC void simsimd_dot_u8_serial(simsimd_u8_t const* a, simsimd_u8_t co
|
|
|
61
61
|
* For single-precision computation check out the "*_serial" counterparts of those "*_accurate" functions.
|
|
62
62
|
*/
|
|
63
63
|
SIMSIMD_PUBLIC void simsimd_dot_f32_accurate(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
64
|
-
SIMSIMD_PUBLIC void simsimd_dot_f32c_accurate(
|
|
65
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f32c_accurate(
|
|
64
|
+
SIMSIMD_PUBLIC void simsimd_dot_f32c_accurate(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
65
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f32c_accurate(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
66
66
|
|
|
67
67
|
SIMSIMD_PUBLIC void simsimd_dot_f16_accurate(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
68
|
-
SIMSIMD_PUBLIC void simsimd_dot_f16c_accurate(
|
|
69
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f16c_accurate(
|
|
68
|
+
SIMSIMD_PUBLIC void simsimd_dot_f16c_accurate(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
69
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f16c_accurate(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
70
70
|
|
|
71
71
|
SIMSIMD_PUBLIC void simsimd_dot_bf16_accurate(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
72
|
-
SIMSIMD_PUBLIC void simsimd_dot_bf16c_accurate(
|
|
73
|
-
SIMSIMD_PUBLIC void simsimd_vdot_bf16c_accurate(
|
|
72
|
+
SIMSIMD_PUBLIC void simsimd_dot_bf16c_accurate(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
73
|
+
SIMSIMD_PUBLIC void simsimd_vdot_bf16c_accurate(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
74
74
|
|
|
75
75
|
/* SIMD-powered backends for Arm NEON, mostly using 32-bit arithmetic over 128-bit words.
|
|
76
76
|
* By far the most portable backend, covering most Arm v8 devices, over a billion phones, and almost all
|
|
77
77
|
* server CPUs produced before 2023.
|
|
78
78
|
*/
|
|
79
79
|
SIMSIMD_PUBLIC void simsimd_dot_f32_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
80
|
-
SIMSIMD_PUBLIC void simsimd_dot_f32c_neon(
|
|
81
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f32c_neon(
|
|
80
|
+
SIMSIMD_PUBLIC void simsimd_dot_f32c_neon(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
81
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f32c_neon(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
82
82
|
|
|
83
83
|
SIMSIMD_PUBLIC void simsimd_dot_f16_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
84
|
-
SIMSIMD_PUBLIC void simsimd_dot_f16c_neon(
|
|
85
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(
|
|
84
|
+
SIMSIMD_PUBLIC void simsimd_dot_f16c_neon(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
85
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
86
86
|
|
|
87
87
|
SIMSIMD_PUBLIC void simsimd_dot_i8_neon(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
88
88
|
SIMSIMD_PUBLIC void simsimd_dot_u8_neon(simsimd_u8_t const* a, simsimd_u8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
89
89
|
|
|
90
90
|
SIMSIMD_PUBLIC void simsimd_dot_bf16_neon(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
91
|
-
SIMSIMD_PUBLIC void simsimd_dot_bf16c_neon(
|
|
92
|
-
SIMSIMD_PUBLIC void simsimd_vdot_bf16c_neon(
|
|
91
|
+
SIMSIMD_PUBLIC void simsimd_dot_bf16c_neon(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
92
|
+
SIMSIMD_PUBLIC void simsimd_vdot_bf16c_neon(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
93
93
|
|
|
94
94
|
/* SIMD-powered backends for Arm SVE, mostly using 32-bit arithmetic over variable-length platform-defined word sizes.
|
|
95
95
|
* Designed for Arm Graviton 3, Microsoft Cobalt, as well as Nvidia Grace and newer Ampere Altra CPUs.
|
|
96
96
|
*/
|
|
97
97
|
SIMSIMD_PUBLIC void simsimd_dot_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
98
|
-
SIMSIMD_PUBLIC void simsimd_dot_f32c_sve(
|
|
99
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f32c_sve(
|
|
98
|
+
SIMSIMD_PUBLIC void simsimd_dot_f32c_sve(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
99
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f32c_sve(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
100
100
|
|
|
101
101
|
SIMSIMD_PUBLIC void simsimd_dot_f16_sve(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
102
|
-
SIMSIMD_PUBLIC void simsimd_dot_f16c_sve(
|
|
103
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f16c_sve(
|
|
102
|
+
SIMSIMD_PUBLIC void simsimd_dot_f16c_sve(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
103
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f16c_sve(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
104
104
|
|
|
105
105
|
SIMSIMD_PUBLIC void simsimd_dot_f64_sve(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
106
|
-
SIMSIMD_PUBLIC void simsimd_dot_f64c_sve(
|
|
107
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(
|
|
106
|
+
SIMSIMD_PUBLIC void simsimd_dot_f64c_sve(simsimd_f64c_t const* a, simsimd_f64c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
107
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64c_t const* a, simsimd_f64c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
108
108
|
|
|
109
109
|
/* SIMD-powered backends for AVX2 CPUs of Haswell generation and newer, using 32-bit arithmetic over 256-bit words.
|
|
110
110
|
* First demonstrated in 2011, at least one Haswell-based processor was still being sold in 2022 — the Pentium G3420.
|
|
@@ -113,12 +113,12 @@ SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64_t const* a, simsimd_f64_t
|
|
|
113
113
|
* properly vectorized by recent compilers.
|
|
114
114
|
*/
|
|
115
115
|
SIMSIMD_PUBLIC void simsimd_dot_f32_haswell(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
116
|
-
SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(
|
|
117
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f32c_haswell(
|
|
116
|
+
SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
117
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f32c_haswell(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
118
118
|
|
|
119
119
|
SIMSIMD_PUBLIC void simsimd_dot_f16_haswell(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
120
|
-
SIMSIMD_PUBLIC void simsimd_dot_f16c_haswell(
|
|
121
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f16c_haswell(
|
|
120
|
+
SIMSIMD_PUBLIC void simsimd_dot_f16c_haswell(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
121
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f16c_haswell(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
122
122
|
|
|
123
123
|
SIMSIMD_PUBLIC void simsimd_dot_bf16_haswell(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
124
124
|
|
|
@@ -130,25 +130,35 @@ SIMSIMD_PUBLIC void simsimd_dot_u8_haswell(simsimd_u8_t const* a, simsimd_u8_t c
|
|
|
130
130
|
* Ice Lake added VNNI, VPOPCNTDQ, IFMA, VBMI, VAES, GFNI, VBMI2, BITALG, VPCLMULQDQ, and other extensions for integral operations.
|
|
131
131
|
* Genoa added only BF16.
|
|
132
132
|
* Sapphire Rapids added tiled matrix operations, but we are most interested in the new mixed-precision FMA instructions.
|
|
133
|
+
*
|
|
134
|
+
* Sadly, we can't effectively interleave different kinds of arithmetic instructions to utilize more ports:
|
|
135
|
+
*
|
|
136
|
+
* > Like Intel server architectures since Skylake-X, SPR cores feature two 512-bit FMA units, and organize them in a similar fashion.
|
|
137
|
+
* > One 512-bit FMA unit is created by fusing two 256-bit ones on port 0 and port 1. The other is added to port 5, as a server-specific
|
|
138
|
+
* > core extension. The FMA units on port 0 and 1 are configured into 2×256-bit or 1×512-bit mode depending on whether 512-bit FMA
|
|
139
|
+
* > instructions are present in the scheduler. That means a mix of 256-bit and 512-bit FMA instructions will not achieve higher IPC
|
|
140
|
+
* > than executing 512-bit instructions alone.
|
|
141
|
+
*
|
|
142
|
+
* Source: https://chipsandcheese.com/p/a-peek-at-sapphire-rapids
|
|
133
143
|
*/
|
|
134
144
|
SIMSIMD_PUBLIC void simsimd_dot_f64_skylake(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
135
|
-
SIMSIMD_PUBLIC void simsimd_dot_f64c_skylake(
|
|
136
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f64c_skylake(
|
|
145
|
+
SIMSIMD_PUBLIC void simsimd_dot_f64c_skylake(simsimd_f64c_t const* a, simsimd_f64c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
146
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f64c_skylake(simsimd_f64c_t const* a, simsimd_f64c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
137
147
|
|
|
138
148
|
SIMSIMD_PUBLIC void simsimd_dot_f32_skylake(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
139
|
-
SIMSIMD_PUBLIC void simsimd_dot_f32c_skylake(
|
|
140
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f32c_skylake(
|
|
149
|
+
SIMSIMD_PUBLIC void simsimd_dot_f32c_skylake(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
150
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f32c_skylake(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
141
151
|
|
|
142
152
|
SIMSIMD_PUBLIC void simsimd_dot_i8_ice(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
143
153
|
SIMSIMD_PUBLIC void simsimd_dot_u8_ice(simsimd_u8_t const* a, simsimd_u8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
144
154
|
|
|
145
155
|
SIMSIMD_PUBLIC void simsimd_dot_bf16_genoa(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
146
|
-
SIMSIMD_PUBLIC void simsimd_dot_bf16c_genoa(
|
|
147
|
-
SIMSIMD_PUBLIC void simsimd_vdot_bf16c_genoa(
|
|
156
|
+
SIMSIMD_PUBLIC void simsimd_dot_bf16c_genoa(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
157
|
+
SIMSIMD_PUBLIC void simsimd_vdot_bf16c_genoa(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
148
158
|
|
|
149
159
|
SIMSIMD_PUBLIC void simsimd_dot_f16_sapphire(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
150
|
-
SIMSIMD_PUBLIC void simsimd_dot_f16c_sapphire(
|
|
151
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f16c_sapphire(
|
|
160
|
+
SIMSIMD_PUBLIC void simsimd_dot_f16c_sapphire(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
161
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f16c_sapphire(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
|
|
152
162
|
|
|
153
163
|
SIMSIMD_PUBLIC void simsimd_dot_i8_sierra(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
|
|
154
164
|
// clang-format on
|
|
@@ -166,70 +176,70 @@ SIMSIMD_PUBLIC void simsimd_dot_i8_sierra(simsimd_i8_t const* a, simsimd_i8_t co
|
|
|
166
176
|
*result = ab; \
|
|
167
177
|
}
|
|
168
178
|
|
|
169
|
-
#define SIMSIMD_MAKE_COMPLEX_DOT(name, input_type, accumulator_type, load_and_convert)
|
|
170
|
-
SIMSIMD_PUBLIC void simsimd_dot_##input_type##
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
simsimd_##accumulator_type##_t ab_real = 0, ab_imag = 0;
|
|
174
|
-
for (simsimd_size_t i = 0; i
|
|
175
|
-
simsimd_##accumulator_type##_t ar = load_and_convert(
|
|
176
|
-
simsimd_##accumulator_type##_t br = load_and_convert(
|
|
177
|
-
simsimd_##accumulator_type##_t ai = load_and_convert(
|
|
178
|
-
simsimd_##accumulator_type##_t bi = load_and_convert(
|
|
179
|
-
ab_real += ar * br - ai * bi;
|
|
180
|
-
ab_imag += ar * bi + ai * br;
|
|
181
|
-
}
|
|
182
|
-
results[0] = ab_real;
|
|
183
|
-
results[1] = ab_imag;
|
|
179
|
+
#define SIMSIMD_MAKE_COMPLEX_DOT(name, input_type, accumulator_type, load_and_convert) \
|
|
180
|
+
SIMSIMD_PUBLIC void simsimd_dot_##input_type##_##name(simsimd_##input_type##_t const *a_pairs, \
|
|
181
|
+
simsimd_##input_type##_t const *b_pairs, \
|
|
182
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) { \
|
|
183
|
+
simsimd_##accumulator_type##_t ab_real = 0, ab_imag = 0; \
|
|
184
|
+
for (simsimd_size_t i = 0; i != count_pairs; ++i) { \
|
|
185
|
+
simsimd_##accumulator_type##_t ar = load_and_convert(&(a_pairs + i)->real); \
|
|
186
|
+
simsimd_##accumulator_type##_t br = load_and_convert(&(b_pairs + i)->real); \
|
|
187
|
+
simsimd_##accumulator_type##_t ai = load_and_convert(&(a_pairs + i)->imag); \
|
|
188
|
+
simsimd_##accumulator_type##_t bi = load_and_convert(&(b_pairs + i)->imag); \
|
|
189
|
+
ab_real += ar * br - ai * bi; \
|
|
190
|
+
ab_imag += ar * bi + ai * br; \
|
|
191
|
+
} \
|
|
192
|
+
results[0] = ab_real; \
|
|
193
|
+
results[1] = ab_imag; \
|
|
184
194
|
}
|
|
185
195
|
|
|
186
|
-
#define SIMSIMD_MAKE_COMPLEX_VDOT(name, input_type, accumulator_type, load_and_convert)
|
|
187
|
-
SIMSIMD_PUBLIC void simsimd_vdot_##input_type##
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
simsimd_##accumulator_type##_t ab_real = 0, ab_imag = 0;
|
|
191
|
-
for (simsimd_size_t i = 0; i
|
|
192
|
-
simsimd_##accumulator_type##_t ar = load_and_convert(
|
|
193
|
-
simsimd_##accumulator_type##_t br = load_and_convert(
|
|
194
|
-
simsimd_##accumulator_type##_t ai = load_and_convert(
|
|
195
|
-
simsimd_##accumulator_type##_t bi = load_and_convert(
|
|
196
|
-
ab_real += ar * br + ai * bi;
|
|
197
|
-
ab_imag += ar * bi - ai * br;
|
|
198
|
-
}
|
|
199
|
-
results[0] = ab_real;
|
|
200
|
-
results[1] = ab_imag;
|
|
196
|
+
#define SIMSIMD_MAKE_COMPLEX_VDOT(name, input_type, accumulator_type, load_and_convert) \
|
|
197
|
+
SIMSIMD_PUBLIC void simsimd_vdot_##input_type##_##name(simsimd_##input_type##_t const *a_pairs, \
|
|
198
|
+
simsimd_##input_type##_t const *b_pairs, \
|
|
199
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) { \
|
|
200
|
+
simsimd_##accumulator_type##_t ab_real = 0, ab_imag = 0; \
|
|
201
|
+
for (simsimd_size_t i = 0; i != count_pairs; ++i) { \
|
|
202
|
+
simsimd_##accumulator_type##_t ar = load_and_convert(&(a_pairs + i)->real); \
|
|
203
|
+
simsimd_##accumulator_type##_t br = load_and_convert(&(b_pairs + i)->real); \
|
|
204
|
+
simsimd_##accumulator_type##_t ai = load_and_convert(&(a_pairs + i)->imag); \
|
|
205
|
+
simsimd_##accumulator_type##_t bi = load_and_convert(&(b_pairs + i)->imag); \
|
|
206
|
+
ab_real += ar * br + ai * bi; \
|
|
207
|
+
ab_imag += ar * bi - ai * br; \
|
|
208
|
+
} \
|
|
209
|
+
results[0] = ab_real; \
|
|
210
|
+
results[1] = ab_imag; \
|
|
201
211
|
}
|
|
202
212
|
|
|
203
|
-
SIMSIMD_MAKE_DOT(serial, f64, f64, SIMSIMD_DEREFERENCE)
|
|
204
|
-
SIMSIMD_MAKE_COMPLEX_DOT(serial,
|
|
205
|
-
SIMSIMD_MAKE_COMPLEX_VDOT(serial,
|
|
213
|
+
SIMSIMD_MAKE_DOT(serial, f64, f64, SIMSIMD_DEREFERENCE) // simsimd_dot_f64_serial
|
|
214
|
+
SIMSIMD_MAKE_COMPLEX_DOT(serial, f64c, f64, SIMSIMD_DEREFERENCE) // simsimd_dot_f64c_serial
|
|
215
|
+
SIMSIMD_MAKE_COMPLEX_VDOT(serial, f64c, f64, SIMSIMD_DEREFERENCE) // simsimd_vdot_f64c_serial
|
|
206
216
|
|
|
207
|
-
SIMSIMD_MAKE_DOT(serial, f32, f32, SIMSIMD_DEREFERENCE)
|
|
208
|
-
SIMSIMD_MAKE_COMPLEX_DOT(serial,
|
|
209
|
-
SIMSIMD_MAKE_COMPLEX_VDOT(serial,
|
|
217
|
+
SIMSIMD_MAKE_DOT(serial, f32, f32, SIMSIMD_DEREFERENCE) // simsimd_dot_f32_serial
|
|
218
|
+
SIMSIMD_MAKE_COMPLEX_DOT(serial, f32c, f32, SIMSIMD_DEREFERENCE) // simsimd_dot_f32c_serial
|
|
219
|
+
SIMSIMD_MAKE_COMPLEX_VDOT(serial, f32c, f32, SIMSIMD_DEREFERENCE) // simsimd_vdot_f32c_serial
|
|
210
220
|
|
|
211
|
-
SIMSIMD_MAKE_DOT(serial, f16, f32, SIMSIMD_F16_TO_F32)
|
|
212
|
-
SIMSIMD_MAKE_COMPLEX_DOT(serial,
|
|
213
|
-
SIMSIMD_MAKE_COMPLEX_VDOT(serial,
|
|
221
|
+
SIMSIMD_MAKE_DOT(serial, f16, f32, SIMSIMD_F16_TO_F32) // simsimd_dot_f16_serial
|
|
222
|
+
SIMSIMD_MAKE_COMPLEX_DOT(serial, f16c, f32, SIMSIMD_F16_TO_F32) // simsimd_dot_f16c_serial
|
|
223
|
+
SIMSIMD_MAKE_COMPLEX_VDOT(serial, f16c, f32, SIMSIMD_F16_TO_F32) // simsimd_vdot_f16c_serial
|
|
214
224
|
|
|
215
|
-
SIMSIMD_MAKE_DOT(serial, bf16, f32, SIMSIMD_BF16_TO_F32)
|
|
216
|
-
SIMSIMD_MAKE_COMPLEX_DOT(serial,
|
|
217
|
-
SIMSIMD_MAKE_COMPLEX_VDOT(serial,
|
|
225
|
+
SIMSIMD_MAKE_DOT(serial, bf16, f32, SIMSIMD_BF16_TO_F32) // simsimd_dot_bf16_serial
|
|
226
|
+
SIMSIMD_MAKE_COMPLEX_DOT(serial, bf16c, f32, SIMSIMD_BF16_TO_F32) // simsimd_dot_bf16c_serial
|
|
227
|
+
SIMSIMD_MAKE_COMPLEX_VDOT(serial, bf16c, f32, SIMSIMD_BF16_TO_F32) // simsimd_vdot_bf16c_serial
|
|
218
228
|
|
|
219
229
|
SIMSIMD_MAKE_DOT(serial, i8, i64, SIMSIMD_DEREFERENCE) // simsimd_dot_i8_serial
|
|
220
230
|
SIMSIMD_MAKE_DOT(serial, u8, i64, SIMSIMD_DEREFERENCE) // simsimd_dot_u8_serial
|
|
221
231
|
|
|
222
|
-
SIMSIMD_MAKE_DOT(accurate, f32, f64, SIMSIMD_DEREFERENCE)
|
|
223
|
-
SIMSIMD_MAKE_COMPLEX_DOT(accurate,
|
|
224
|
-
SIMSIMD_MAKE_COMPLEX_VDOT(accurate,
|
|
232
|
+
SIMSIMD_MAKE_DOT(accurate, f32, f64, SIMSIMD_DEREFERENCE) // simsimd_dot_f32_accurate
|
|
233
|
+
SIMSIMD_MAKE_COMPLEX_DOT(accurate, f32c, f64, SIMSIMD_DEREFERENCE) // simsimd_dot_f32c_accurate
|
|
234
|
+
SIMSIMD_MAKE_COMPLEX_VDOT(accurate, f32c, f64, SIMSIMD_DEREFERENCE) // simsimd_vdot_f32c_accurate
|
|
225
235
|
|
|
226
|
-
SIMSIMD_MAKE_DOT(accurate, f16, f64, SIMSIMD_F16_TO_F32)
|
|
227
|
-
SIMSIMD_MAKE_COMPLEX_DOT(accurate,
|
|
228
|
-
SIMSIMD_MAKE_COMPLEX_VDOT(accurate,
|
|
236
|
+
SIMSIMD_MAKE_DOT(accurate, f16, f64, SIMSIMD_F16_TO_F32) // simsimd_dot_f16_accurate
|
|
237
|
+
SIMSIMD_MAKE_COMPLEX_DOT(accurate, f16c, f64, SIMSIMD_F16_TO_F32) // simsimd_dot_f16c_accurate
|
|
238
|
+
SIMSIMD_MAKE_COMPLEX_VDOT(accurate, f16c, f64, SIMSIMD_F16_TO_F32) // simsimd_vdot_f16c_accurate
|
|
229
239
|
|
|
230
|
-
SIMSIMD_MAKE_DOT(accurate, bf16, f64, SIMSIMD_BF16_TO_F32)
|
|
231
|
-
SIMSIMD_MAKE_COMPLEX_DOT(accurate,
|
|
232
|
-
SIMSIMD_MAKE_COMPLEX_VDOT(accurate,
|
|
240
|
+
SIMSIMD_MAKE_DOT(accurate, bf16, f64, SIMSIMD_BF16_TO_F32) // simsimd_dot_bf16_accurate
|
|
241
|
+
SIMSIMD_MAKE_COMPLEX_DOT(accurate, bf16c, f64, SIMSIMD_BF16_TO_F32) // simsimd_dot_bf16c_accurate
|
|
242
|
+
SIMSIMD_MAKE_COMPLEX_VDOT(accurate, bf16c, f64, SIMSIMD_BF16_TO_F32) // simsimd_vdot_bf16c_accurate
|
|
233
243
|
|
|
234
244
|
#if _SIMSIMD_TARGET_ARM
|
|
235
245
|
#if SIMSIMD_TARGET_NEON
|
|
@@ -237,40 +247,40 @@ SIMSIMD_MAKE_COMPLEX_VDOT(accurate, bf16, f64, SIMSIMD_BF16_TO_F32) // simsimd_v
|
|
|
237
247
|
#pragma GCC target("arch=armv8.2-a+simd")
|
|
238
248
|
#pragma clang attribute push(__attribute__((target("arch=armv8.2-a+simd"))), apply_to = function)
|
|
239
249
|
|
|
240
|
-
SIMSIMD_INTERNAL float32x4_t _simsimd_partial_load_f32x4_neon(simsimd_f32_t const *
|
|
250
|
+
SIMSIMD_INTERNAL float32x4_t _simsimd_partial_load_f32x4_neon(simsimd_f32_t const *x, simsimd_size_t n) {
|
|
241
251
|
union {
|
|
242
252
|
float32x4_t vec;
|
|
243
253
|
simsimd_f32_t scalars[4];
|
|
244
254
|
} result;
|
|
245
255
|
simsimd_size_t i = 0;
|
|
246
|
-
for (; i < n; ++i) result.scalars[i] =
|
|
256
|
+
for (; i < n; ++i) result.scalars[i] = x[i];
|
|
247
257
|
for (; i < 4; ++i) result.scalars[i] = 0;
|
|
248
258
|
return result.vec;
|
|
249
259
|
}
|
|
250
260
|
|
|
251
|
-
SIMSIMD_PUBLIC void simsimd_dot_f32_neon(simsimd_f32_t const *
|
|
252
|
-
simsimd_distance_t *result) {
|
|
261
|
+
SIMSIMD_PUBLIC void simsimd_dot_f32_neon(simsimd_f32_t const *a_scalars, simsimd_f32_t const *b_scalars,
|
|
262
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
253
263
|
float32x4_t ab_vec = vdupq_n_f32(0);
|
|
254
|
-
simsimd_size_t
|
|
255
|
-
for (;
|
|
256
|
-
float32x4_t a_vec = vld1q_f32(
|
|
257
|
-
float32x4_t b_vec = vld1q_f32(
|
|
264
|
+
simsimd_size_t idx_scalars = 0;
|
|
265
|
+
for (; idx_scalars + 4 <= count_scalars; idx_scalars += 4) {
|
|
266
|
+
float32x4_t a_vec = vld1q_f32(a_scalars + idx_scalars);
|
|
267
|
+
float32x4_t b_vec = vld1q_f32(b_scalars + idx_scalars);
|
|
258
268
|
ab_vec = vfmaq_f32(ab_vec, a_vec, b_vec);
|
|
259
269
|
}
|
|
260
270
|
simsimd_f32_t ab = vaddvq_f32(ab_vec);
|
|
261
|
-
for (;
|
|
271
|
+
for (; idx_scalars < count_scalars; ++idx_scalars) ab += a_scalars[idx_scalars] * b_scalars[idx_scalars];
|
|
262
272
|
*result = ab;
|
|
263
273
|
}
|
|
264
274
|
|
|
265
|
-
SIMSIMD_PUBLIC void simsimd_dot_f32c_neon(
|
|
266
|
-
simsimd_distance_t *results) {
|
|
275
|
+
SIMSIMD_PUBLIC void simsimd_dot_f32c_neon(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
|
|
276
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
267
277
|
float32x4_t ab_real_vec = vdupq_n_f32(0);
|
|
268
278
|
float32x4_t ab_imag_vec = vdupq_n_f32(0);
|
|
269
|
-
simsimd_size_t
|
|
270
|
-
for (;
|
|
279
|
+
simsimd_size_t idx_pairs = 0;
|
|
280
|
+
for (; idx_pairs + 4 <= count_pairs; idx_pairs += 4) {
|
|
271
281
|
// Unpack the input arrays into real and imaginary parts:
|
|
272
|
-
float32x4x2_t a_vec = vld2q_f32(
|
|
273
|
-
float32x4x2_t b_vec = vld2q_f32(
|
|
282
|
+
float32x4x2_t a_vec = vld2q_f32((simsimd_f32_t const *)(a_pairs + idx_pairs));
|
|
283
|
+
float32x4x2_t b_vec = vld2q_f32((simsimd_f32_t const *)(b_pairs + idx_pairs));
|
|
274
284
|
float32x4_t a_real_vec = a_vec.val[0];
|
|
275
285
|
float32x4_t a_imag_vec = a_vec.val[1];
|
|
276
286
|
float32x4_t b_real_vec = b_vec.val[0];
|
|
@@ -288,8 +298,9 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c_neon(simsimd_f32_t const *a, simsimd_f32_t
|
|
|
288
298
|
simsimd_f32_t ab_imag = vaddvq_f32(ab_imag_vec);
|
|
289
299
|
|
|
290
300
|
// Handle the tail:
|
|
291
|
-
for (;
|
|
292
|
-
|
|
301
|
+
for (; idx_pairs != count_pairs; ++idx_pairs) {
|
|
302
|
+
simsimd_f32c_t a_pair = a_pairs[idx_pairs], b_pair = b_pairs[idx_pairs];
|
|
303
|
+
simsimd_f32_t ar = a_pair.real, ai = a_pair.imag, br = b_pair.real, bi = b_pair.imag;
|
|
293
304
|
ab_real += ar * br - ai * bi;
|
|
294
305
|
ab_imag += ar * bi + ai * br;
|
|
295
306
|
}
|
|
@@ -297,15 +308,15 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c_neon(simsimd_f32_t const *a, simsimd_f32_t
|
|
|
297
308
|
results[1] = ab_imag;
|
|
298
309
|
}
|
|
299
310
|
|
|
300
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f32c_neon(
|
|
301
|
-
simsimd_distance_t *results) {
|
|
311
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f32c_neon(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
|
|
312
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
302
313
|
float32x4_t ab_real_vec = vdupq_n_f32(0);
|
|
303
314
|
float32x4_t ab_imag_vec = vdupq_n_f32(0);
|
|
304
|
-
simsimd_size_t
|
|
305
|
-
for (;
|
|
315
|
+
simsimd_size_t idx_pairs = 0;
|
|
316
|
+
for (; idx_pairs + 4 <= count_pairs; idx_pairs += 4) {
|
|
306
317
|
// Unpack the input arrays into real and imaginary parts:
|
|
307
|
-
float32x4x2_t a_vec = vld2q_f32(
|
|
308
|
-
float32x4x2_t b_vec = vld2q_f32(
|
|
318
|
+
float32x4x2_t a_vec = vld2q_f32((simsimd_f32_t const *)(a_pairs + idx_pairs));
|
|
319
|
+
float32x4x2_t b_vec = vld2q_f32((simsimd_f32_t const *)(b_pairs + idx_pairs));
|
|
309
320
|
float32x4_t a_real_vec = a_vec.val[0];
|
|
310
321
|
float32x4_t a_imag_vec = a_vec.val[1];
|
|
311
322
|
float32x4_t b_real_vec = b_vec.val[0];
|
|
@@ -323,8 +334,9 @@ SIMSIMD_PUBLIC void simsimd_vdot_f32c_neon(simsimd_f32_t const *a, simsimd_f32_t
|
|
|
323
334
|
simsimd_f32_t ab_imag = vaddvq_f32(ab_imag_vec);
|
|
324
335
|
|
|
325
336
|
// Handle the tail:
|
|
326
|
-
for (;
|
|
327
|
-
|
|
337
|
+
for (; idx_pairs != count_pairs; ++idx_pairs) {
|
|
338
|
+
simsimd_f32c_t a_pair = a_pairs[idx_pairs], b_pair = b_pairs[idx_pairs];
|
|
339
|
+
simsimd_f32_t ar = a_pair.real, ai = a_pair.imag, br = b_pair.real, bi = b_pair.imag;
|
|
328
340
|
ab_real += ar * br + ai * bi;
|
|
329
341
|
ab_imag += ar * bi - ai * br;
|
|
330
342
|
}
|
|
@@ -341,51 +353,51 @@ SIMSIMD_PUBLIC void simsimd_vdot_f32c_neon(simsimd_f32_t const *a, simsimd_f32_t
|
|
|
341
353
|
#pragma GCC target("arch=armv8.2-a+dotprod")
|
|
342
354
|
#pragma clang attribute push(__attribute__((target("arch=armv8.2-a+dotprod"))), apply_to = function)
|
|
343
355
|
|
|
344
|
-
SIMSIMD_PUBLIC void simsimd_dot_i8_neon(simsimd_i8_t const *
|
|
345
|
-
simsimd_distance_t *result) {
|
|
356
|
+
SIMSIMD_PUBLIC void simsimd_dot_i8_neon(simsimd_i8_t const *a_scalars, simsimd_i8_t const *b_scalars,
|
|
357
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
346
358
|
|
|
347
359
|
int32x4_t ab_vec = vdupq_n_s32(0);
|
|
348
|
-
simsimd_size_t
|
|
360
|
+
simsimd_size_t idx_scalars = 0;
|
|
349
361
|
|
|
350
362
|
// If the 128-bit `vdot_s32` intrinsic is unavailable, we can use the 64-bit `vdot_s32`.
|
|
351
|
-
// for (simsimd_size_t
|
|
352
|
-
// int16x8_t a_vec = vmovl_s8(vld1_s8(
|
|
353
|
-
// int16x8_t b_vec = vmovl_s8(vld1_s8(
|
|
363
|
+
// for (simsimd_size_t idx_scalars = 0; idx_scalars != n; idx_scalars += 8) {
|
|
364
|
+
// int16x8_t a_vec = vmovl_s8(vld1_s8(a_scalars + idx_scalars));
|
|
365
|
+
// int16x8_t b_vec = vmovl_s8(vld1_s8(b_scalars + idx_scalars));
|
|
354
366
|
// int16x8_t ab_part_vec = vmulq_s16(a_vec, b_vec);
|
|
355
367
|
// ab_vec = vaddq_s32(ab_vec, vaddq_s32(vmovl_s16(vget_high_s16(ab_part_vec)), //
|
|
356
368
|
// vmovl_s16(vget_low_s16(ab_part_vec))));
|
|
357
369
|
// }
|
|
358
|
-
for (;
|
|
359
|
-
int8x16_t a_vec = vld1q_s8(
|
|
360
|
-
int8x16_t b_vec = vld1q_s8(
|
|
370
|
+
for (; idx_scalars + 16 <= count_scalars; idx_scalars += 16) {
|
|
371
|
+
int8x16_t a_vec = vld1q_s8(a_scalars + idx_scalars);
|
|
372
|
+
int8x16_t b_vec = vld1q_s8(b_scalars + idx_scalars);
|
|
361
373
|
ab_vec = vdotq_s32(ab_vec, a_vec, b_vec);
|
|
362
374
|
}
|
|
363
375
|
|
|
364
376
|
// Take care of the tail:
|
|
365
|
-
|
|
366
|
-
for (;
|
|
367
|
-
|
|
377
|
+
simsimd_i32_t ab = vaddvq_s32(ab_vec);
|
|
378
|
+
for (; idx_scalars < count_scalars; ++idx_scalars) {
|
|
379
|
+
simsimd_i32_t ai = a_scalars[idx_scalars], bi = b_scalars[idx_scalars];
|
|
368
380
|
ab += ai * bi;
|
|
369
381
|
}
|
|
370
382
|
|
|
371
383
|
*result = ab;
|
|
372
384
|
}
|
|
373
385
|
|
|
374
|
-
SIMSIMD_PUBLIC void simsimd_dot_u8_neon(simsimd_u8_t const *
|
|
375
|
-
simsimd_distance_t *result) {
|
|
386
|
+
SIMSIMD_PUBLIC void simsimd_dot_u8_neon(simsimd_u8_t const *a_scalars, simsimd_u8_t const *b_scalars,
|
|
387
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
376
388
|
|
|
377
389
|
uint32x4_t ab_vec = vdupq_n_u32(0);
|
|
378
|
-
simsimd_size_t
|
|
379
|
-
for (;
|
|
380
|
-
uint8x16_t a_vec = vld1q_u8(
|
|
381
|
-
uint8x16_t b_vec = vld1q_u8(
|
|
390
|
+
simsimd_size_t idx_scalars = 0;
|
|
391
|
+
for (; idx_scalars + 16 <= count_scalars; idx_scalars += 16) {
|
|
392
|
+
uint8x16_t a_vec = vld1q_u8(a_scalars + idx_scalars);
|
|
393
|
+
uint8x16_t b_vec = vld1q_u8(b_scalars + idx_scalars);
|
|
382
394
|
ab_vec = vdotq_u32(ab_vec, a_vec, b_vec);
|
|
383
395
|
}
|
|
384
396
|
|
|
385
397
|
// Take care of the tail:
|
|
386
|
-
|
|
387
|
-
for (;
|
|
388
|
-
|
|
398
|
+
simsimd_u32_t ab = vaddvq_u32(ab_vec);
|
|
399
|
+
for (; idx_scalars < count_scalars; ++idx_scalars) {
|
|
400
|
+
simsimd_u32_t ai = a_scalars[idx_scalars], bi = b_scalars[idx_scalars];
|
|
389
401
|
ab += ai * bi;
|
|
390
402
|
}
|
|
391
403
|
|
|
@@ -401,7 +413,7 @@ SIMSIMD_PUBLIC void simsimd_dot_u8_neon(simsimd_u8_t const *a, simsimd_u8_t cons
|
|
|
401
413
|
#pragma GCC target("arch=armv8.2-a+simd+fp16")
|
|
402
414
|
#pragma clang attribute push(__attribute__((target("arch=armv8.2-a+simd+fp16"))), apply_to = function)
|
|
403
415
|
|
|
404
|
-
SIMSIMD_INTERNAL float16x4_t _simsimd_partial_load_f16x4_neon(simsimd_f16_t const *
|
|
416
|
+
SIMSIMD_INTERNAL float16x4_t _simsimd_partial_load_f16x4_neon(simsimd_f16_t const *x, simsimd_size_t n) {
|
|
405
417
|
// In case the software emulation for `f16` scalars is enabled, the `simsimd_f16_to_f32`
|
|
406
418
|
// function will run. It is extremely slow, so even for the tail, let's combine serial
|
|
407
419
|
// loads and stores with vectorized math.
|
|
@@ -410,35 +422,35 @@ SIMSIMD_INTERNAL float16x4_t _simsimd_partial_load_f16x4_neon(simsimd_f16_t cons
|
|
|
410
422
|
simsimd_f16_t scalars[4];
|
|
411
423
|
} result;
|
|
412
424
|
simsimd_size_t i = 0;
|
|
413
|
-
for (; i < n; ++i) result.scalars[i] =
|
|
425
|
+
for (; i < n; ++i) result.scalars[i] = x[i];
|
|
414
426
|
for (; i < 4; ++i) result.scalars[i] = 0;
|
|
415
427
|
return result.vec;
|
|
416
428
|
}
|
|
417
429
|
|
|
418
|
-
SIMSIMD_PUBLIC void simsimd_dot_f16_neon(simsimd_f16_t const *
|
|
419
|
-
simsimd_distance_t *result) {
|
|
420
|
-
float32x4_t ab_vec = vdupq_n_f32(0);
|
|
430
|
+
SIMSIMD_PUBLIC void simsimd_dot_f16_neon(simsimd_f16_t const *a_scalars, simsimd_f16_t const *b_scalars,
|
|
431
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
421
432
|
float32x4_t a_vec, b_vec;
|
|
433
|
+
float32x4_t ab_vec = vdupq_n_f32(0);
|
|
422
434
|
simsimd_size_t i = 0;
|
|
423
435
|
|
|
424
436
|
simsimd_dot_f16_neon_cycle:
|
|
425
|
-
if (
|
|
426
|
-
a_vec = vcvt_f32_f16(_simsimd_partial_load_f16x4_neon(
|
|
427
|
-
b_vec = vcvt_f32_f16(_simsimd_partial_load_f16x4_neon(
|
|
428
|
-
|
|
437
|
+
if (count_scalars < 4) {
|
|
438
|
+
a_vec = vcvt_f32_f16(_simsimd_partial_load_f16x4_neon(a_scalars, count_scalars));
|
|
439
|
+
b_vec = vcvt_f32_f16(_simsimd_partial_load_f16x4_neon(b_scalars, count_scalars));
|
|
440
|
+
count_scalars = 0;
|
|
429
441
|
}
|
|
430
442
|
else {
|
|
431
|
-
a_vec = vcvt_f32_f16(vld1_f16((simsimd_f16_for_arm_simd_t const *)
|
|
432
|
-
b_vec = vcvt_f32_f16(vld1_f16((simsimd_f16_for_arm_simd_t const *)
|
|
433
|
-
|
|
443
|
+
a_vec = vcvt_f32_f16(vld1_f16((simsimd_f16_for_arm_simd_t const *)a_scalars));
|
|
444
|
+
b_vec = vcvt_f32_f16(vld1_f16((simsimd_f16_for_arm_simd_t const *)b_scalars));
|
|
445
|
+
a_scalars += 4, b_scalars += 4, count_scalars -= 4;
|
|
434
446
|
}
|
|
435
447
|
ab_vec = vfmaq_f32(ab_vec, a_vec, b_vec);
|
|
436
|
-
if (
|
|
448
|
+
if (count_scalars) goto simsimd_dot_f16_neon_cycle;
|
|
437
449
|
*result = vaddvq_f32(ab_vec);
|
|
438
450
|
}
|
|
439
451
|
|
|
440
|
-
SIMSIMD_PUBLIC void simsimd_dot_f16c_neon(
|
|
441
|
-
simsimd_distance_t *results) {
|
|
452
|
+
SIMSIMD_PUBLIC void simsimd_dot_f16c_neon(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
|
|
453
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
442
454
|
|
|
443
455
|
// A nicer approach is to use `f16` arithmetic for the dot product, but that requires
|
|
444
456
|
// FMLA extensions available on Arm v8.3 and later. That we can also process 16 entries
|
|
@@ -446,12 +458,12 @@ SIMSIMD_PUBLIC void simsimd_dot_f16c_neon(simsimd_f16_t const *a, simsimd_f16_t
|
|
|
446
458
|
float32x4_t ab_real_vec = vdupq_n_f32(0);
|
|
447
459
|
float32x4_t ab_imag_vec = vdupq_n_f32(0);
|
|
448
460
|
|
|
449
|
-
while (
|
|
461
|
+
while (count_pairs >= 4) {
|
|
450
462
|
// Unpack the input arrays into real and imaginary parts.
|
|
451
463
|
// MSVC sadly doesn't recognize the `vld2_f16`, so we load the data as signed
|
|
452
464
|
// integers of the same size and reinterpret with `vreinterpret_f16_s16` afterwards.
|
|
453
|
-
int16x4x2_t a_vec = vld2_s16((short *)
|
|
454
|
-
int16x4x2_t b_vec = vld2_s16((short *)
|
|
465
|
+
int16x4x2_t a_vec = vld2_s16((short *)a_pairs);
|
|
466
|
+
int16x4x2_t b_vec = vld2_s16((short *)b_pairs);
|
|
455
467
|
float32x4_t a_real_vec = vcvt_f32_f16(vreinterpret_f16_s16(a_vec.val[0]));
|
|
456
468
|
float32x4_t a_imag_vec = vcvt_f32_f16(vreinterpret_f16_s16(a_vec.val[1]));
|
|
457
469
|
float32x4_t b_real_vec = vcvt_f32_f16(vreinterpret_f16_s16(b_vec.val[0]));
|
|
@@ -463,17 +475,17 @@ SIMSIMD_PUBLIC void simsimd_dot_f16c_neon(simsimd_f16_t const *a, simsimd_f16_t
|
|
|
463
475
|
ab_imag_vec = vfmaq_f32(ab_imag_vec, a_real_vec, b_imag_vec);
|
|
464
476
|
ab_imag_vec = vfmaq_f32(ab_imag_vec, a_imag_vec, b_real_vec);
|
|
465
477
|
|
|
466
|
-
|
|
478
|
+
count_pairs -= 4, a_pairs += 4, b_pairs += 4;
|
|
467
479
|
}
|
|
468
480
|
|
|
469
481
|
// Reduce horizontal sums and aggregate with the tail:
|
|
470
|
-
simsimd_dot_f16c_serial(
|
|
482
|
+
simsimd_dot_f16c_serial(a_pairs, b_pairs, count_pairs, results);
|
|
471
483
|
results[0] += vaddvq_f32(ab_real_vec);
|
|
472
484
|
results[1] += vaddvq_f32(ab_imag_vec);
|
|
473
485
|
}
|
|
474
486
|
|
|
475
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(
|
|
476
|
-
simsimd_distance_t *results) {
|
|
487
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
|
|
488
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
477
489
|
|
|
478
490
|
// A nicer approach is to use `f16` arithmetic for the dot product, but that requires
|
|
479
491
|
// FMLA extensions available on Arm v8.3 and later. That we can also process 16 entries
|
|
@@ -481,12 +493,12 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(simsimd_f16_t const *a, simsimd_f16_t
|
|
|
481
493
|
float32x4_t ab_real_vec = vdupq_n_f32(0);
|
|
482
494
|
float32x4_t ab_imag_vec = vdupq_n_f32(0);
|
|
483
495
|
|
|
484
|
-
while (
|
|
496
|
+
while (count_pairs >= 4) {
|
|
485
497
|
// Unpack the input arrays into real and imaginary parts.
|
|
486
498
|
// MSVC sadly doesn't recognize the `vld2_f16`, so we load the data as signed
|
|
487
499
|
// integers of the same size and reinterpret with `vreinterpret_f16_s16` afterwards.
|
|
488
|
-
int16x4x2_t a_vec = vld2_s16((short *)
|
|
489
|
-
int16x4x2_t b_vec = vld2_s16((short *)
|
|
500
|
+
int16x4x2_t a_vec = vld2_s16((short *)a_pairs);
|
|
501
|
+
int16x4x2_t b_vec = vld2_s16((short *)b_pairs);
|
|
490
502
|
float32x4_t a_real_vec = vcvt_f32_f16(vreinterpret_f16_s16(a_vec.val[0]));
|
|
491
503
|
float32x4_t a_imag_vec = vcvt_f32_f16(vreinterpret_f16_s16(a_vec.val[1]));
|
|
492
504
|
float32x4_t b_real_vec = vcvt_f32_f16(vreinterpret_f16_s16(b_vec.val[0]));
|
|
@@ -498,11 +510,11 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(simsimd_f16_t const *a, simsimd_f16_t
|
|
|
498
510
|
ab_imag_vec = vfmaq_f32(ab_imag_vec, a_real_vec, b_imag_vec);
|
|
499
511
|
ab_imag_vec = vfmsq_f32(ab_imag_vec, a_imag_vec, b_real_vec);
|
|
500
512
|
|
|
501
|
-
|
|
513
|
+
count_pairs -= 4, a_pairs += 4, b_pairs += 4;
|
|
502
514
|
}
|
|
503
515
|
|
|
504
516
|
// Reduce horizontal sums and aggregate with the tail:
|
|
505
|
-
simsimd_vdot_f16c_serial(
|
|
517
|
+
simsimd_vdot_f16c_serial(a_pairs, b_pairs, count_pairs, results);
|
|
506
518
|
results[0] += vaddvq_f32(ab_real_vec);
|
|
507
519
|
results[1] += vaddvq_f32(ab_imag_vec);
|
|
508
520
|
}
|
|
@@ -516,41 +528,41 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(simsimd_f16_t const *a, simsimd_f16_t
|
|
|
516
528
|
#pragma GCC target("arch=armv8.6-a+simd+bf16")
|
|
517
529
|
#pragma clang attribute push(__attribute__((target("arch=armv8.6-a+simd+bf16"))), apply_to = function)
|
|
518
530
|
|
|
519
|
-
SIMSIMD_INTERNAL bfloat16x8_t _simsimd_partial_load_bf16x8_neon(simsimd_bf16_t const *
|
|
531
|
+
SIMSIMD_INTERNAL bfloat16x8_t _simsimd_partial_load_bf16x8_neon(simsimd_bf16_t const *x, simsimd_size_t n) {
|
|
520
532
|
union {
|
|
521
533
|
bfloat16x8_t vec;
|
|
522
534
|
simsimd_bf16_t scalars[8];
|
|
523
535
|
} result;
|
|
524
536
|
simsimd_size_t i = 0;
|
|
525
|
-
for (; i < n; ++i) result.scalars[i] =
|
|
537
|
+
for (; i < n; ++i) result.scalars[i] = x[i];
|
|
526
538
|
for (; i < 8; ++i) result.scalars[i] = 0;
|
|
527
539
|
return result.vec;
|
|
528
540
|
}
|
|
529
541
|
|
|
530
|
-
SIMSIMD_PUBLIC void simsimd_dot_bf16_neon(simsimd_bf16_t const *
|
|
531
|
-
simsimd_distance_t *result) {
|
|
532
|
-
float32x4_t ab_vec = vdupq_n_f32(0);
|
|
542
|
+
SIMSIMD_PUBLIC void simsimd_dot_bf16_neon(simsimd_bf16_t const *a_scalars, simsimd_bf16_t const *b_scalars,
|
|
543
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
533
544
|
bfloat16x8_t a_vec, b_vec;
|
|
545
|
+
float32x4_t ab_vec = vdupq_n_f32(0);
|
|
534
546
|
|
|
535
547
|
simsimd_dot_bf16_neon_cycle:
|
|
536
|
-
if (
|
|
537
|
-
a_vec = _simsimd_partial_load_bf16x8_neon(
|
|
538
|
-
b_vec = _simsimd_partial_load_bf16x8_neon(
|
|
539
|
-
|
|
548
|
+
if (count_scalars < 4) {
|
|
549
|
+
a_vec = _simsimd_partial_load_bf16x8_neon(a_scalars, count_scalars);
|
|
550
|
+
b_vec = _simsimd_partial_load_bf16x8_neon(b_scalars, count_scalars);
|
|
551
|
+
count_scalars = 0;
|
|
540
552
|
}
|
|
541
553
|
else {
|
|
542
|
-
a_vec = vld1q_bf16((simsimd_bf16_for_arm_simd_t const *)
|
|
543
|
-
b_vec = vld1q_bf16((simsimd_bf16_for_arm_simd_t const *)
|
|
544
|
-
|
|
554
|
+
a_vec = vld1q_bf16((simsimd_bf16_for_arm_simd_t const *)a_scalars);
|
|
555
|
+
b_vec = vld1q_bf16((simsimd_bf16_for_arm_simd_t const *)b_scalars);
|
|
556
|
+
a_scalars += 4, b_scalars += 4, count_scalars -= 4;
|
|
545
557
|
}
|
|
546
558
|
ab_vec = vbfdotq_f32(ab_vec, a_vec, b_vec);
|
|
547
|
-
if (
|
|
559
|
+
if (count_scalars) goto simsimd_dot_bf16_neon_cycle;
|
|
548
560
|
|
|
549
561
|
*result = vaddvq_f32(ab_vec);
|
|
550
562
|
}
|
|
551
563
|
|
|
552
|
-
SIMSIMD_PUBLIC void simsimd_dot_bf16c_neon(
|
|
553
|
-
simsimd_distance_t *results) {
|
|
564
|
+
SIMSIMD_PUBLIC void simsimd_dot_bf16c_neon(simsimd_bf16c_t const *a_pairs, simsimd_bf16c_t const *b_pairs,
|
|
565
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
554
566
|
|
|
555
567
|
// A nicer approach is to use `bf16` arithmetic for the dot product, but that requires
|
|
556
568
|
// FMLA extensions available on Arm v8.3 and later. That we can also process 16 entries
|
|
@@ -558,12 +570,12 @@ SIMSIMD_PUBLIC void simsimd_dot_bf16c_neon(simsimd_bf16_t const *a, simsimd_bf16
|
|
|
558
570
|
float32x4_t ab_real_vec = vdupq_n_f32(0);
|
|
559
571
|
float32x4_t ab_imag_vec = vdupq_n_f32(0);
|
|
560
572
|
|
|
561
|
-
while (
|
|
573
|
+
while (count_pairs >= 4) {
|
|
562
574
|
// Unpack the input arrays into real and imaginary parts.
|
|
563
575
|
// MSVC sadly doesn't recognize the `vld2_bf16`, so we load the data as signed
|
|
564
576
|
// integers of the same size and reinterpret with `vreinterpret_bf16_s16` afterwards.
|
|
565
|
-
int16x4x2_t a_vec = vld2_s16((short *)
|
|
566
|
-
int16x4x2_t b_vec = vld2_s16((short *)
|
|
577
|
+
int16x4x2_t a_vec = vld2_s16((short const *)a_pairs);
|
|
578
|
+
int16x4x2_t b_vec = vld2_s16((short const *)b_pairs);
|
|
567
579
|
float32x4_t a_real_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(a_vec.val[0]));
|
|
568
580
|
float32x4_t a_imag_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(a_vec.val[1]));
|
|
569
581
|
float32x4_t b_real_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(b_vec.val[0]));
|
|
@@ -575,17 +587,17 @@ SIMSIMD_PUBLIC void simsimd_dot_bf16c_neon(simsimd_bf16_t const *a, simsimd_bf16
|
|
|
575
587
|
ab_imag_vec = vfmaq_f32(ab_imag_vec, a_real_vec, b_imag_vec);
|
|
576
588
|
ab_imag_vec = vfmaq_f32(ab_imag_vec, a_imag_vec, b_real_vec);
|
|
577
589
|
|
|
578
|
-
|
|
590
|
+
count_pairs -= 4, a_pairs += 4, b_pairs += 4;
|
|
579
591
|
}
|
|
580
592
|
|
|
581
593
|
// Reduce horizontal sums and aggregate with the tail:
|
|
582
|
-
simsimd_dot_bf16c_serial(
|
|
594
|
+
simsimd_dot_bf16c_serial(a_pairs, b_pairs, count_pairs, results);
|
|
583
595
|
results[0] += vaddvq_f32(ab_real_vec);
|
|
584
596
|
results[1] += vaddvq_f32(ab_imag_vec);
|
|
585
597
|
}
|
|
586
598
|
|
|
587
|
-
SIMSIMD_PUBLIC void simsimd_vdot_bf16c_neon(
|
|
588
|
-
simsimd_distance_t *results) {
|
|
599
|
+
SIMSIMD_PUBLIC void simsimd_vdot_bf16c_neon(simsimd_bf16c_t const *a_pairs, simsimd_bf16c_t const *b_pairs,
|
|
600
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
589
601
|
|
|
590
602
|
// A nicer approach is to use `bf16` arithmetic for the dot product, but that requires
|
|
591
603
|
// FMLA extensions available on Arm v8.3 and later. That we can also process 16 entries
|
|
@@ -593,12 +605,12 @@ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_neon(simsimd_bf16_t const *a, simsimd_bf1
|
|
|
593
605
|
float32x4_t ab_real_vec = vdupq_n_f32(0);
|
|
594
606
|
float32x4_t ab_imag_vec = vdupq_n_f32(0);
|
|
595
607
|
|
|
596
|
-
while (
|
|
608
|
+
while (count_pairs >= 4) {
|
|
597
609
|
// Unpack the input arrays into real and imaginary parts.
|
|
598
610
|
// MSVC sadly doesn't recognize the `vld2_bf16`, so we load the data as signed
|
|
599
611
|
// integers of the same size and reinterpret with `vreinterpret_bf16_s16` afterwards.
|
|
600
|
-
int16x4x2_t a_vec = vld2_s16((short *)
|
|
601
|
-
int16x4x2_t b_vec = vld2_s16((short *)
|
|
612
|
+
int16x4x2_t a_vec = vld2_s16((short const *)a_pairs);
|
|
613
|
+
int16x4x2_t b_vec = vld2_s16((short const *)b_pairs);
|
|
602
614
|
float32x4_t a_real_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(a_vec.val[0]));
|
|
603
615
|
float32x4_t a_imag_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(a_vec.val[1]));
|
|
604
616
|
float32x4_t b_real_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(b_vec.val[0]));
|
|
@@ -610,11 +622,11 @@ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_neon(simsimd_bf16_t const *a, simsimd_bf1
|
|
|
610
622
|
ab_imag_vec = vfmaq_f32(ab_imag_vec, a_real_vec, b_imag_vec);
|
|
611
623
|
ab_imag_vec = vfmsq_f32(ab_imag_vec, a_imag_vec, b_real_vec);
|
|
612
624
|
|
|
613
|
-
|
|
625
|
+
count_pairs -= 4, a_pairs += 4, b_pairs += 4;
|
|
614
626
|
}
|
|
615
627
|
|
|
616
628
|
// Reduce horizontal sums and aggregate with the tail:
|
|
617
|
-
simsimd_vdot_bf16c_serial(
|
|
629
|
+
simsimd_vdot_bf16c_serial(a_pairs, b_pairs, count_pairs, results);
|
|
618
630
|
results[0] += vaddvq_f32(ab_real_vec);
|
|
619
631
|
results[1] += vaddvq_f32(ab_imag_vec);
|
|
620
632
|
}
|
|
@@ -629,29 +641,29 @@ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_neon(simsimd_bf16_t const *a, simsimd_bf1
|
|
|
629
641
|
#pragma GCC target("arch=armv8.2-a+sve")
|
|
630
642
|
#pragma clang attribute push(__attribute__((target("arch=armv8.2-a+sve"))), apply_to = function)
|
|
631
643
|
|
|
632
|
-
SIMSIMD_PUBLIC void simsimd_dot_f32_sve(simsimd_f32_t const *
|
|
633
|
-
simsimd_distance_t *result) {
|
|
634
|
-
simsimd_size_t
|
|
644
|
+
SIMSIMD_PUBLIC void simsimd_dot_f32_sve(simsimd_f32_t const *a_scalars, simsimd_f32_t const *b_scalars,
|
|
645
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
646
|
+
simsimd_size_t idx_scalars = 0;
|
|
635
647
|
svfloat32_t ab_vec = svdup_f32(0.f);
|
|
636
648
|
do {
|
|
637
|
-
svbool_t pg_vec = svwhilelt_b32((unsigned int)
|
|
638
|
-
svfloat32_t a_vec = svld1_f32(pg_vec,
|
|
639
|
-
svfloat32_t b_vec = svld1_f32(pg_vec,
|
|
649
|
+
svbool_t pg_vec = svwhilelt_b32((unsigned int)idx_scalars, (unsigned int)count_scalars);
|
|
650
|
+
svfloat32_t a_vec = svld1_f32(pg_vec, a_scalars + idx_scalars);
|
|
651
|
+
svfloat32_t b_vec = svld1_f32(pg_vec, b_scalars + idx_scalars);
|
|
640
652
|
ab_vec = svmla_f32_x(pg_vec, ab_vec, a_vec, b_vec);
|
|
641
|
-
|
|
642
|
-
} while (
|
|
653
|
+
idx_scalars += svcntw();
|
|
654
|
+
} while (idx_scalars < count_scalars);
|
|
643
655
|
*result = svaddv_f32(svptrue_b32(), ab_vec);
|
|
644
656
|
}
|
|
645
657
|
|
|
646
|
-
SIMSIMD_PUBLIC void simsimd_dot_f32c_sve(
|
|
647
|
-
simsimd_distance_t *results) {
|
|
648
|
-
simsimd_size_t
|
|
658
|
+
SIMSIMD_PUBLIC void simsimd_dot_f32c_sve(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
|
|
659
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
660
|
+
simsimd_size_t idx_pairs = 0;
|
|
649
661
|
svfloat32_t ab_real_vec = svdup_f32(0.f);
|
|
650
662
|
svfloat32_t ab_imag_vec = svdup_f32(0.f);
|
|
651
663
|
do {
|
|
652
|
-
svbool_t pg_vec = svwhilelt_b32((unsigned int)
|
|
653
|
-
svfloat32x2_t a_vec = svld2_f32(pg_vec,
|
|
654
|
-
svfloat32x2_t b_vec = svld2_f32(pg_vec,
|
|
664
|
+
svbool_t pg_vec = svwhilelt_b32((unsigned int)idx_pairs, (unsigned int)count_pairs);
|
|
665
|
+
svfloat32x2_t a_vec = svld2_f32(pg_vec, (simsimd_f32_t const *)(a_pairs + idx_pairs));
|
|
666
|
+
svfloat32x2_t b_vec = svld2_f32(pg_vec, (simsimd_f32_t const *)(b_pairs + idx_pairs));
|
|
655
667
|
svfloat32_t a_real_vec = svget2_f32(a_vec, 0);
|
|
656
668
|
svfloat32_t a_imag_vec = svget2_f32(a_vec, 1);
|
|
657
669
|
svfloat32_t b_real_vec = svget2_f32(b_vec, 0);
|
|
@@ -660,21 +672,21 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c_sve(simsimd_f32_t const *a, simsimd_f32_t c
|
|
|
660
672
|
ab_real_vec = svmls_f32_x(pg_vec, ab_real_vec, a_imag_vec, b_imag_vec);
|
|
661
673
|
ab_imag_vec = svmla_f32_x(pg_vec, ab_imag_vec, a_real_vec, b_imag_vec);
|
|
662
674
|
ab_imag_vec = svmla_f32_x(pg_vec, ab_imag_vec, a_imag_vec, b_real_vec);
|
|
663
|
-
|
|
664
|
-
} while (
|
|
675
|
+
idx_pairs += svcntw();
|
|
676
|
+
} while (idx_pairs < count_pairs);
|
|
665
677
|
results[0] = svaddv_f32(svptrue_b32(), ab_real_vec);
|
|
666
678
|
results[1] = svaddv_f32(svptrue_b32(), ab_imag_vec);
|
|
667
679
|
}
|
|
668
680
|
|
|
669
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f32c_sve(
|
|
670
|
-
simsimd_distance_t *results) {
|
|
671
|
-
simsimd_size_t
|
|
681
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f32c_sve(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
|
|
682
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
683
|
+
simsimd_size_t idx_pairs = 0;
|
|
672
684
|
svfloat32_t ab_real_vec = svdup_f32(0.f);
|
|
673
685
|
svfloat32_t ab_imag_vec = svdup_f32(0.f);
|
|
674
686
|
do {
|
|
675
|
-
svbool_t pg_vec = svwhilelt_b32((unsigned int)
|
|
676
|
-
svfloat32x2_t a_vec = svld2_f32(pg_vec,
|
|
677
|
-
svfloat32x2_t b_vec = svld2_f32(pg_vec,
|
|
687
|
+
svbool_t pg_vec = svwhilelt_b32((unsigned int)idx_pairs, (unsigned int)count_pairs);
|
|
688
|
+
svfloat32x2_t a_vec = svld2_f32(pg_vec, (simsimd_f32_t const *)(a_pairs + idx_pairs));
|
|
689
|
+
svfloat32x2_t b_vec = svld2_f32(pg_vec, (simsimd_f32_t const *)(b_pairs + idx_pairs));
|
|
678
690
|
svfloat32_t a_real_vec = svget2_f32(a_vec, 0);
|
|
679
691
|
svfloat32_t a_imag_vec = svget2_f32(a_vec, 1);
|
|
680
692
|
svfloat32_t b_real_vec = svget2_f32(b_vec, 0);
|
|
@@ -683,35 +695,35 @@ SIMSIMD_PUBLIC void simsimd_vdot_f32c_sve(simsimd_f32_t const *a, simsimd_f32_t
|
|
|
683
695
|
ab_real_vec = svmla_f32_x(pg_vec, ab_real_vec, a_imag_vec, b_imag_vec);
|
|
684
696
|
ab_imag_vec = svmla_f32_x(pg_vec, ab_imag_vec, a_real_vec, b_imag_vec);
|
|
685
697
|
ab_imag_vec = svmls_f32_x(pg_vec, ab_imag_vec, a_imag_vec, b_real_vec);
|
|
686
|
-
|
|
687
|
-
} while (
|
|
698
|
+
idx_pairs += svcntw();
|
|
699
|
+
} while (idx_pairs < count_pairs);
|
|
688
700
|
results[0] = svaddv_f32(svptrue_b32(), ab_real_vec);
|
|
689
701
|
results[1] = svaddv_f32(svptrue_b32(), ab_imag_vec);
|
|
690
702
|
}
|
|
691
703
|
|
|
692
|
-
SIMSIMD_PUBLIC void simsimd_dot_f64_sve(simsimd_f64_t const *
|
|
693
|
-
simsimd_distance_t *result) {
|
|
694
|
-
simsimd_size_t
|
|
704
|
+
SIMSIMD_PUBLIC void simsimd_dot_f64_sve(simsimd_f64_t const *a_scalars, simsimd_f64_t const *b_scalars,
|
|
705
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
706
|
+
simsimd_size_t idx_scalars = 0;
|
|
695
707
|
svfloat64_t ab_vec = svdup_f64(0.);
|
|
696
708
|
do {
|
|
697
|
-
svbool_t pg_vec = svwhilelt_b64((unsigned int)
|
|
698
|
-
svfloat64_t a_vec = svld1_f64(pg_vec,
|
|
699
|
-
svfloat64_t b_vec = svld1_f64(pg_vec,
|
|
709
|
+
svbool_t pg_vec = svwhilelt_b64((unsigned int)idx_scalars, (unsigned int)count_scalars);
|
|
710
|
+
svfloat64_t a_vec = svld1_f64(pg_vec, a_scalars + idx_scalars);
|
|
711
|
+
svfloat64_t b_vec = svld1_f64(pg_vec, b_scalars + idx_scalars);
|
|
700
712
|
ab_vec = svmla_f64_x(pg_vec, ab_vec, a_vec, b_vec);
|
|
701
|
-
|
|
702
|
-
} while (
|
|
713
|
+
idx_scalars += svcntd();
|
|
714
|
+
} while (idx_scalars < count_scalars);
|
|
703
715
|
*result = svaddv_f64(svptrue_b32(), ab_vec);
|
|
704
716
|
}
|
|
705
717
|
|
|
706
|
-
SIMSIMD_PUBLIC void simsimd_dot_f64c_sve(
|
|
707
|
-
simsimd_distance_t *results) {
|
|
708
|
-
simsimd_size_t
|
|
718
|
+
SIMSIMD_PUBLIC void simsimd_dot_f64c_sve(simsimd_f64c_t const *a_pairs, simsimd_f64c_t const *b_pairs,
|
|
719
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
720
|
+
simsimd_size_t idx_pairs = 0;
|
|
709
721
|
svfloat64_t ab_real_vec = svdup_f64(0.);
|
|
710
722
|
svfloat64_t ab_imag_vec = svdup_f64(0.);
|
|
711
723
|
do {
|
|
712
|
-
svbool_t pg_vec = svwhilelt_b64((unsigned int)
|
|
713
|
-
svfloat64x2_t a_vec = svld2_f64(pg_vec,
|
|
714
|
-
svfloat64x2_t b_vec = svld2_f64(pg_vec,
|
|
724
|
+
svbool_t pg_vec = svwhilelt_b64((unsigned int)idx_pairs, (unsigned int)count_pairs);
|
|
725
|
+
svfloat64x2_t a_vec = svld2_f64(pg_vec, (simsimd_f64_t const *)(a_pairs + idx_pairs));
|
|
726
|
+
svfloat64x2_t b_vec = svld2_f64(pg_vec, (simsimd_f64_t const *)(b_pairs + idx_pairs));
|
|
715
727
|
svfloat64_t a_real_vec = svget2_f64(a_vec, 0);
|
|
716
728
|
svfloat64_t a_imag_vec = svget2_f64(a_vec, 1);
|
|
717
729
|
svfloat64_t b_real_vec = svget2_f64(b_vec, 0);
|
|
@@ -720,21 +732,21 @@ SIMSIMD_PUBLIC void simsimd_dot_f64c_sve(simsimd_f64_t const *a, simsimd_f64_t c
|
|
|
720
732
|
ab_real_vec = svmls_f64_x(pg_vec, ab_real_vec, a_imag_vec, b_imag_vec);
|
|
721
733
|
ab_imag_vec = svmla_f64_x(pg_vec, ab_imag_vec, a_real_vec, b_imag_vec);
|
|
722
734
|
ab_imag_vec = svmla_f64_x(pg_vec, ab_imag_vec, a_imag_vec, b_real_vec);
|
|
723
|
-
|
|
724
|
-
} while (
|
|
735
|
+
idx_pairs += svcntd();
|
|
736
|
+
} while (idx_pairs < count_pairs);
|
|
725
737
|
results[0] = svaddv_f64(svptrue_b64(), ab_real_vec);
|
|
726
738
|
results[1] = svaddv_f64(svptrue_b64(), ab_imag_vec);
|
|
727
739
|
}
|
|
728
740
|
|
|
729
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(
|
|
730
|
-
simsimd_distance_t *results) {
|
|
731
|
-
simsimd_size_t
|
|
741
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64c_t const *a_pairs, simsimd_f64c_t const *b_pairs,
|
|
742
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
743
|
+
simsimd_size_t idx_pairs = 0;
|
|
732
744
|
svfloat64_t ab_real_vec = svdup_f64(0.);
|
|
733
745
|
svfloat64_t ab_imag_vec = svdup_f64(0.);
|
|
734
746
|
do {
|
|
735
|
-
svbool_t pg_vec = svwhilelt_b64((unsigned int)
|
|
736
|
-
svfloat64x2_t a_vec = svld2_f64(pg_vec,
|
|
737
|
-
svfloat64x2_t b_vec = svld2_f64(pg_vec,
|
|
747
|
+
svbool_t pg_vec = svwhilelt_b64((unsigned int)idx_pairs, (unsigned int)count_pairs);
|
|
748
|
+
svfloat64x2_t a_vec = svld2_f64(pg_vec, (simsimd_f64_t const *)(a_pairs + idx_pairs));
|
|
749
|
+
svfloat64x2_t b_vec = svld2_f64(pg_vec, (simsimd_f64_t const *)(b_pairs + idx_pairs));
|
|
738
750
|
svfloat64_t a_real_vec = svget2_f64(a_vec, 0);
|
|
739
751
|
svfloat64_t a_imag_vec = svget2_f64(a_vec, 1);
|
|
740
752
|
svfloat64_t b_real_vec = svget2_f64(b_vec, 0);
|
|
@@ -743,8 +755,8 @@ SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64_t const *a, simsimd_f64_t
|
|
|
743
755
|
ab_real_vec = svmla_f64_x(pg_vec, ab_real_vec, a_imag_vec, b_imag_vec);
|
|
744
756
|
ab_imag_vec = svmla_f64_x(pg_vec, ab_imag_vec, a_real_vec, b_imag_vec);
|
|
745
757
|
ab_imag_vec = svmls_f64_x(pg_vec, ab_imag_vec, a_imag_vec, b_real_vec);
|
|
746
|
-
|
|
747
|
-
} while (
|
|
758
|
+
idx_pairs += svcntd();
|
|
759
|
+
} while (idx_pairs < count_pairs);
|
|
748
760
|
results[0] = svaddv_f64(svptrue_b64(), ab_real_vec);
|
|
749
761
|
results[1] = svaddv_f64(svptrue_b64(), ab_imag_vec);
|
|
750
762
|
}
|
|
@@ -756,32 +768,30 @@ SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64_t const *a, simsimd_f64_t
|
|
|
756
768
|
#pragma GCC target("arch=armv8.2-a+sve+fp16")
|
|
757
769
|
#pragma clang attribute push(__attribute__((target("arch=armv8.2-a+sve+fp16"))), apply_to = function)
|
|
758
770
|
|
|
759
|
-
SIMSIMD_PUBLIC void simsimd_dot_f16_sve(simsimd_f16_t const *
|
|
760
|
-
simsimd_distance_t *result) {
|
|
761
|
-
simsimd_size_t
|
|
771
|
+
SIMSIMD_PUBLIC void simsimd_dot_f16_sve(simsimd_f16_t const *a_scalars, simsimd_f16_t const *b_scalars,
|
|
772
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
773
|
+
simsimd_size_t idx_scalars = 0;
|
|
762
774
|
svfloat16_t ab_vec = svdup_f16(0);
|
|
763
|
-
simsimd_f16_for_arm_simd_t const *a = (simsimd_f16_for_arm_simd_t const *)(a_enum);
|
|
764
|
-
simsimd_f16_for_arm_simd_t const *b = (simsimd_f16_for_arm_simd_t const *)(b_enum);
|
|
765
775
|
do {
|
|
766
|
-
svbool_t pg_vec = svwhilelt_b16((unsigned int)
|
|
767
|
-
svfloat16_t a_vec = svld1_f16(pg_vec,
|
|
768
|
-
svfloat16_t b_vec = svld1_f16(pg_vec,
|
|
776
|
+
svbool_t pg_vec = svwhilelt_b16((unsigned int)idx_scalars, (unsigned int)count_scalars);
|
|
777
|
+
svfloat16_t a_vec = svld1_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)(a_scalars + idx_scalars));
|
|
778
|
+
svfloat16_t b_vec = svld1_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)(b_scalars + idx_scalars));
|
|
769
779
|
ab_vec = svmla_f16_x(pg_vec, ab_vec, a_vec, b_vec);
|
|
770
|
-
|
|
771
|
-
} while (
|
|
780
|
+
idx_scalars += svcnth();
|
|
781
|
+
} while (idx_scalars < count_scalars);
|
|
772
782
|
simsimd_f16_for_arm_simd_t ab = svaddv_f16(svptrue_b16(), ab_vec);
|
|
773
783
|
*result = ab;
|
|
774
784
|
}
|
|
775
785
|
|
|
776
|
-
SIMSIMD_PUBLIC void simsimd_dot_f16c_sve(
|
|
777
|
-
simsimd_distance_t *results) {
|
|
778
|
-
simsimd_size_t
|
|
786
|
+
SIMSIMD_PUBLIC void simsimd_dot_f16c_sve(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
|
|
787
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
788
|
+
simsimd_size_t idx_pairs = 0;
|
|
779
789
|
svfloat16_t ab_real_vec = svdup_f16(0);
|
|
780
790
|
svfloat16_t ab_imag_vec = svdup_f16(0);
|
|
781
791
|
do {
|
|
782
|
-
svbool_t pg_vec =
|
|
783
|
-
svfloat16x2_t a_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)
|
|
784
|
-
svfloat16x2_t b_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)
|
|
792
|
+
svbool_t pg_vec = svwhilelt_b32((unsigned int)idx_pairs, (unsigned int)count_pairs);
|
|
793
|
+
svfloat16x2_t a_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)(a_pairs + idx_pairs));
|
|
794
|
+
svfloat16x2_t b_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)(b_pairs + idx_pairs));
|
|
785
795
|
svfloat16_t a_real_vec = svget2_f16(a_vec, 0);
|
|
786
796
|
svfloat16_t a_imag_vec = svget2_f16(a_vec, 1);
|
|
787
797
|
svfloat16_t b_real_vec = svget2_f16(b_vec, 0);
|
|
@@ -790,21 +800,21 @@ SIMSIMD_PUBLIC void simsimd_dot_f16c_sve(simsimd_f16_t const *a, simsimd_f16_t c
|
|
|
790
800
|
ab_real_vec = svmls_f16_x(pg_vec, ab_real_vec, a_imag_vec, b_imag_vec);
|
|
791
801
|
ab_imag_vec = svmla_f16_x(pg_vec, ab_imag_vec, a_real_vec, b_imag_vec);
|
|
792
802
|
ab_imag_vec = svmla_f16_x(pg_vec, ab_imag_vec, a_imag_vec, b_real_vec);
|
|
793
|
-
|
|
794
|
-
} while (
|
|
803
|
+
idx_pairs += svcnth();
|
|
804
|
+
} while (idx_pairs < count_pairs);
|
|
795
805
|
results[0] = svaddv_f16(svptrue_b16(), ab_real_vec);
|
|
796
806
|
results[1] = svaddv_f16(svptrue_b16(), ab_imag_vec);
|
|
797
807
|
}
|
|
798
808
|
|
|
799
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f16c_sve(
|
|
800
|
-
simsimd_distance_t *results) {
|
|
801
|
-
simsimd_size_t
|
|
809
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f16c_sve(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
|
|
810
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
811
|
+
simsimd_size_t idx_pairs = 0;
|
|
802
812
|
svfloat16_t ab_real_vec = svdup_f16(0);
|
|
803
813
|
svfloat16_t ab_imag_vec = svdup_f16(0);
|
|
804
814
|
do {
|
|
805
|
-
svbool_t pg_vec =
|
|
806
|
-
svfloat16x2_t a_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)
|
|
807
|
-
svfloat16x2_t b_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)
|
|
815
|
+
svbool_t pg_vec = svwhilelt_b32((unsigned int)idx_pairs, (unsigned int)count_pairs);
|
|
816
|
+
svfloat16x2_t a_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)(a_pairs + idx_pairs));
|
|
817
|
+
svfloat16x2_t b_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)(b_pairs + idx_pairs));
|
|
808
818
|
svfloat16_t a_real_vec = svget2_f16(a_vec, 0);
|
|
809
819
|
svfloat16_t a_imag_vec = svget2_f16(a_vec, 1);
|
|
810
820
|
svfloat16_t b_real_vec = svget2_f16(b_vec, 0);
|
|
@@ -813,8 +823,8 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c_sve(simsimd_f16_t const *a, simsimd_f16_t
|
|
|
813
823
|
ab_real_vec = svmla_f16_x(pg_vec, ab_real_vec, a_imag_vec, b_imag_vec);
|
|
814
824
|
ab_imag_vec = svmla_f16_x(pg_vec, ab_imag_vec, a_real_vec, b_imag_vec);
|
|
815
825
|
ab_imag_vec = svmls_f16_x(pg_vec, ab_imag_vec, a_imag_vec, b_real_vec);
|
|
816
|
-
|
|
817
|
-
} while (
|
|
826
|
+
idx_pairs += svcnth();
|
|
827
|
+
} while (idx_pairs < count_pairs);
|
|
818
828
|
results[0] = svaddv_f16(svptrue_b16(), ab_real_vec);
|
|
819
829
|
results[1] = svaddv_f16(svptrue_b16(), ab_imag_vec);
|
|
820
830
|
}
|
|
@@ -867,23 +877,23 @@ SIMSIMD_INTERNAL simsimd_i32_t _simsimd_reduce_i32x8_haswell(__m256i vec) {
|
|
|
867
877
|
return _mm_cvtsi128_si32(sum);
|
|
868
878
|
}
|
|
869
879
|
|
|
870
|
-
SIMSIMD_PUBLIC void simsimd_dot_f32_haswell(simsimd_f32_t const *
|
|
871
|
-
simsimd_distance_t *results) {
|
|
880
|
+
SIMSIMD_PUBLIC void simsimd_dot_f32_haswell(simsimd_f32_t const *a_scalars, simsimd_f32_t const *b_scalars,
|
|
881
|
+
simsimd_size_t count_scalars, simsimd_distance_t *results) {
|
|
872
882
|
|
|
873
883
|
__m256 ab_vec = _mm256_setzero_ps();
|
|
874
|
-
simsimd_size_t
|
|
875
|
-
for (;
|
|
876
|
-
__m256 a_vec = _mm256_loadu_ps(
|
|
877
|
-
__m256 b_vec = _mm256_loadu_ps(
|
|
884
|
+
simsimd_size_t idx_scalars = 0;
|
|
885
|
+
for (; idx_scalars + 8 <= count_scalars; idx_scalars += 8) {
|
|
886
|
+
__m256 a_vec = _mm256_loadu_ps(a_scalars + idx_scalars);
|
|
887
|
+
__m256 b_vec = _mm256_loadu_ps(b_scalars + idx_scalars);
|
|
878
888
|
ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
|
|
879
889
|
}
|
|
880
890
|
simsimd_f64_t ab = _simsimd_reduce_f32x8_haswell(ab_vec);
|
|
881
|
-
for (;
|
|
891
|
+
for (; idx_scalars < count_scalars; ++idx_scalars) ab += a_scalars[idx_scalars] * b_scalars[idx_scalars];
|
|
882
892
|
*results = ab;
|
|
883
893
|
}
|
|
884
894
|
|
|
885
|
-
SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(
|
|
886
|
-
simsimd_distance_t *results) {
|
|
895
|
+
SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
|
|
896
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
887
897
|
|
|
888
898
|
// The naive approach would be to use FMA and FMS instructions on different parts of the vectors.
|
|
889
899
|
// Prior to that we would need to shuffle the input vectors to separate real and imaginary parts.
|
|
@@ -891,10 +901,10 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32_t const *a, simsimd_f32
|
|
|
891
901
|
// __m128 ab_real_vec = _mm_setzero_ps();
|
|
892
902
|
// __m128 ab_imag_vec = _mm_setzero_ps();
|
|
893
903
|
// __m256i permute_vec = _mm256_set_epi32(7, 5, 3, 1, 6, 4, 2, 0);
|
|
894
|
-
// simsimd_size_t
|
|
895
|
-
// for (;
|
|
896
|
-
// __m256 a_vec = _mm256_loadu_ps(
|
|
897
|
-
// __m256 b_vec = _mm256_loadu_ps(
|
|
904
|
+
// simsimd_size_t idx_pairs = 0;
|
|
905
|
+
// for (; idx_pairs + 4 <= count_pairs; idx_pairs += 4) {
|
|
906
|
+
// __m256 a_vec = _mm256_loadu_ps((simsimd_f32_t const *)(a_pairs + idx_pairs));
|
|
907
|
+
// __m256 b_vec = _mm256_loadu_ps((simsimd_f32_t const *)(b_pairs + idx_pairs));
|
|
898
908
|
// __m256 a_shuffled = _mm256_permutevar8x32_ps(a_vec, permute_vec);
|
|
899
909
|
// __m256 b_shuffled = _mm256_permutevar8x32_ps(b_vec, permute_vec);
|
|
900
910
|
// __m128 a_real_vec = _mm256_extractf128_ps(a_shuffled, 0);
|
|
@@ -926,10 +936,11 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32_t const *a, simsimd_f32
|
|
|
926
936
|
3, 2, 1, 0, // Points to the first f32 in 128-bit lane
|
|
927
937
|
7, 6, 5, 4 // Points to the second f32 in 128-bit lane
|
|
928
938
|
);
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
__m256
|
|
939
|
+
|
|
940
|
+
simsimd_size_t idx_pairs = 0;
|
|
941
|
+
for (; idx_pairs + 4 <= count_pairs; idx_pairs += 4) {
|
|
942
|
+
__m256 a_vec = _mm256_loadu_ps((simsimd_f32_t const *)(a_pairs + idx_pairs));
|
|
943
|
+
__m256 b_vec = _mm256_loadu_ps((simsimd_f32_t const *)(b_pairs + idx_pairs));
|
|
933
944
|
__m256 b_swapped_vec = _mm256_castsi256_ps(_mm256_shuffle_epi8(_mm256_castps_si256(b_vec), swap_adjacent_vec));
|
|
934
945
|
ab_real_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_real_vec);
|
|
935
946
|
ab_imag_vec = _mm256_fmadd_ps(a_vec, b_swapped_vec, ab_imag_vec);
|
|
@@ -943,8 +954,9 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32_t const *a, simsimd_f32
|
|
|
943
954
|
simsimd_distance_t ab_imag = _simsimd_reduce_f32x8_haswell(ab_imag_vec);
|
|
944
955
|
|
|
945
956
|
// Handle the tail:
|
|
946
|
-
for (;
|
|
947
|
-
|
|
957
|
+
for (; idx_pairs != count_pairs; ++idx_pairs) {
|
|
958
|
+
simsimd_f32c_t a_pair = a_pairs[idx_pairs], b_pair = b_pairs[idx_pairs];
|
|
959
|
+
simsimd_f32_t ar = a_pair.real, ai = a_pair.imag, br = b_pair.real, bi = b_pair.imag;
|
|
948
960
|
ab_real += ar * br - ai * bi;
|
|
949
961
|
ab_imag += ar * bi + ai * br;
|
|
950
962
|
}
|
|
@@ -952,8 +964,8 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32_t const *a, simsimd_f32
|
|
|
952
964
|
results[1] = ab_imag;
|
|
953
965
|
}
|
|
954
966
|
|
|
955
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f32c_haswell(
|
|
956
|
-
simsimd_distance_t *results) {
|
|
967
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f32c_haswell(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
|
|
968
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
957
969
|
|
|
958
970
|
__m256 ab_real_vec = _mm256_setzero_ps();
|
|
959
971
|
__m256 ab_imag_vec = _mm256_setzero_ps();
|
|
@@ -968,10 +980,11 @@ SIMSIMD_PUBLIC void simsimd_vdot_f32c_haswell(simsimd_f32_t const *a, simsimd_f3
|
|
|
968
980
|
3, 2, 1, 0, // Points to the first f32 in 128-bit lane
|
|
969
981
|
7, 6, 5, 4 // Points to the second f32 in 128-bit lane
|
|
970
982
|
);
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
__m256
|
|
983
|
+
|
|
984
|
+
simsimd_size_t idx_pairs = 0;
|
|
985
|
+
for (; idx_pairs + 4 <= count_pairs; idx_pairs += 4) {
|
|
986
|
+
__m256 a_vec = _mm256_loadu_ps((simsimd_f32_t const *)(a_pairs + idx_pairs));
|
|
987
|
+
__m256 b_vec = _mm256_loadu_ps((simsimd_f32_t const *)(b_pairs + idx_pairs));
|
|
975
988
|
ab_real_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_real_vec);
|
|
976
989
|
b_vec = _mm256_castsi256_ps(_mm256_shuffle_epi8(_mm256_castps_si256(b_vec), swap_adjacent_vec));
|
|
977
990
|
ab_imag_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_imag_vec);
|
|
@@ -985,8 +998,9 @@ SIMSIMD_PUBLIC void simsimd_vdot_f32c_haswell(simsimd_f32_t const *a, simsimd_f3
|
|
|
985
998
|
simsimd_distance_t ab_imag = _simsimd_reduce_f32x8_haswell(ab_imag_vec);
|
|
986
999
|
|
|
987
1000
|
// Handle the tail:
|
|
988
|
-
for (;
|
|
989
|
-
|
|
1001
|
+
for (; idx_pairs != count_pairs; ++idx_pairs) {
|
|
1002
|
+
simsimd_f32c_t a_pair = a_pairs[idx_pairs], b_pair = b_pairs[idx_pairs];
|
|
1003
|
+
simsimd_f32_t ar = a_pair.real, ai = a_pair.imag, br = b_pair.real, bi = b_pair.imag;
|
|
990
1004
|
ab_real += ar * br + ai * bi;
|
|
991
1005
|
ab_imag += ar * bi - ai * br;
|
|
992
1006
|
}
|
|
@@ -1008,21 +1022,21 @@ SIMSIMD_INTERNAL __m256 _simsimd_partial_load_f16x8_haswell(simsimd_f16_t const
|
|
|
1008
1022
|
return _mm256_cvtph_ps(result.vec);
|
|
1009
1023
|
}
|
|
1010
1024
|
|
|
1011
|
-
SIMSIMD_PUBLIC void simsimd_dot_f16_haswell(simsimd_f16_t const *
|
|
1012
|
-
simsimd_distance_t *result) {
|
|
1025
|
+
SIMSIMD_PUBLIC void simsimd_dot_f16_haswell(simsimd_f16_t const *a_scalars, simsimd_f16_t const *b_scalars,
|
|
1026
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
1013
1027
|
__m256 a_vec, b_vec;
|
|
1014
1028
|
__m256 ab_vec = _mm256_setzero_ps();
|
|
1015
1029
|
|
|
1016
1030
|
simsimd_dot_f16_haswell_cycle:
|
|
1017
|
-
if (
|
|
1018
|
-
a_vec = _simsimd_partial_load_f16x8_haswell(
|
|
1019
|
-
b_vec = _simsimd_partial_load_f16x8_haswell(
|
|
1020
|
-
|
|
1031
|
+
if (count_scalars < 8) {
|
|
1032
|
+
a_vec = _simsimd_partial_load_f16x8_haswell(a_scalars, count_scalars);
|
|
1033
|
+
b_vec = _simsimd_partial_load_f16x8_haswell(b_scalars, count_scalars);
|
|
1034
|
+
count_scalars = 0;
|
|
1021
1035
|
}
|
|
1022
1036
|
else {
|
|
1023
|
-
a_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)
|
|
1024
|
-
b_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)
|
|
1025
|
-
|
|
1037
|
+
a_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)a_scalars));
|
|
1038
|
+
b_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)b_scalars));
|
|
1039
|
+
count_scalars -= 8, a_scalars += 8, b_scalars += 8;
|
|
1026
1040
|
}
|
|
1027
1041
|
// We can silence the NaNs using blends:
|
|
1028
1042
|
//
|
|
@@ -1031,13 +1045,14 @@ simsimd_dot_f16_haswell_cycle:
|
|
|
1031
1045
|
// ab_vec = _mm256_blendv_ps(_mm256_fmadd_ps(a_vec, b_vec, ab_vec), ab_vec, _mm256_or_ps(a_is_nan, b_is_nan));
|
|
1032
1046
|
//
|
|
1033
1047
|
ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
|
|
1034
|
-
if (
|
|
1048
|
+
if (count_scalars) goto simsimd_dot_f16_haswell_cycle;
|
|
1035
1049
|
|
|
1036
1050
|
*result = _simsimd_reduce_f32x8_haswell(ab_vec);
|
|
1037
1051
|
}
|
|
1038
1052
|
|
|
1039
|
-
SIMSIMD_PUBLIC void simsimd_dot_f16c_haswell(
|
|
1040
|
-
simsimd_distance_t *results) {
|
|
1053
|
+
SIMSIMD_PUBLIC void simsimd_dot_f16c_haswell(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
|
|
1054
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
1055
|
+
|
|
1041
1056
|
// Ideally the implementation would load 256 bits worth of vector data at a time,
|
|
1042
1057
|
// shuffle those within a register, split in halfs, and only then upcast.
|
|
1043
1058
|
// That way, we are stepping through 32x 16-bit vector components at a time, or 16 dimensions.
|
|
@@ -1059,28 +1074,34 @@ SIMSIMD_PUBLIC void simsimd_dot_f16c_haswell(simsimd_f16_t const *a, simsimd_f16
|
|
|
1059
1074
|
7, 6, 5, 4 // Points to the second f32 in 128-bit lane
|
|
1060
1075
|
);
|
|
1061
1076
|
|
|
1062
|
-
while (
|
|
1063
|
-
__m256 a_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)
|
|
1064
|
-
__m256 b_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)
|
|
1077
|
+
while (count_pairs >= 4) {
|
|
1078
|
+
__m256 a_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)a_pairs));
|
|
1079
|
+
__m256 b_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)b_pairs));
|
|
1065
1080
|
__m256 b_swapped_vec = _mm256_castsi256_ps(_mm256_shuffle_epi8(_mm256_castps_si256(b_vec), swap_adjacent_vec));
|
|
1066
1081
|
ab_real_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_real_vec);
|
|
1067
1082
|
ab_imag_vec = _mm256_fmadd_ps(a_vec, b_swapped_vec, ab_imag_vec);
|
|
1068
|
-
|
|
1069
|
-
n -= 8, a += 8, b += 8;
|
|
1083
|
+
count_pairs -= 4, a_pairs += 4, b_pairs += 4;
|
|
1070
1084
|
}
|
|
1071
1085
|
|
|
1072
1086
|
// Flip the sign bit in every second scalar before accumulation:
|
|
1073
1087
|
ab_real_vec = _mm256_castsi256_ps(_mm256_xor_si256(_mm256_castps_si256(ab_real_vec), sign_flip_vec));
|
|
1074
1088
|
|
|
1075
1089
|
// Reduce horizontal sums and aggregate with the tail:
|
|
1076
|
-
simsimd_dot_f16c_serial(
|
|
1090
|
+
simsimd_dot_f16c_serial(a_pairs, b_pairs, count_pairs, results);
|
|
1077
1091
|
results[0] += _simsimd_reduce_f32x8_haswell(ab_real_vec);
|
|
1078
1092
|
results[1] += _simsimd_reduce_f32x8_haswell(ab_imag_vec);
|
|
1079
1093
|
}
|
|
1080
1094
|
|
|
1081
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f16c_haswell(
|
|
1082
|
-
simsimd_distance_t *results) {
|
|
1095
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f16c_haswell(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
|
|
1096
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
1083
1097
|
|
|
1098
|
+
// Ideally the implementation would load 256 bits worth of vector data at a time,
|
|
1099
|
+
// shuffle those within a register, split in halfs, and only then upcast.
|
|
1100
|
+
// That way, we are stepping through 32x 16-bit vector components at a time, or 16 dimensions.
|
|
1101
|
+
// Sadly, shuffling 16-bit entries in a YMM register is hard to implement efficiently.
|
|
1102
|
+
//
|
|
1103
|
+
// Simpler approach is to load 128 bits at a time, upcast, and then shuffle.
|
|
1104
|
+
// This mostly replicates the `simsimd_vdot_f32c_haswell`.
|
|
1084
1105
|
__m256 ab_real_vec = _mm256_setzero_ps();
|
|
1085
1106
|
__m256 ab_imag_vec = _mm256_setzero_ps();
|
|
1086
1107
|
__m256i sign_flip_vec = _mm256_set1_epi64x(0x8000000000000000);
|
|
@@ -1095,27 +1116,26 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c_haswell(simsimd_f16_t const *a, simsimd_f1
|
|
|
1095
1116
|
7, 6, 5, 4 // Points to the second f32 in 128-bit lane
|
|
1096
1117
|
);
|
|
1097
1118
|
|
|
1098
|
-
while (
|
|
1099
|
-
__m256 a_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)
|
|
1100
|
-
__m256 b_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)
|
|
1119
|
+
while (count_pairs >= 4) {
|
|
1120
|
+
__m256 a_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)a_pairs));
|
|
1121
|
+
__m256 b_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)b_pairs));
|
|
1101
1122
|
ab_real_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_real_vec);
|
|
1102
1123
|
b_vec = _mm256_castsi256_ps(_mm256_shuffle_epi8(_mm256_castps_si256(b_vec), swap_adjacent_vec));
|
|
1103
1124
|
ab_imag_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_imag_vec);
|
|
1104
|
-
|
|
1105
|
-
n -= 8, a += 8, b += 8;
|
|
1125
|
+
count_pairs -= 4, a_pairs += 4, b_pairs += 4;
|
|
1106
1126
|
}
|
|
1107
1127
|
|
|
1108
1128
|
// Flip the sign bit in every second scalar before accumulation:
|
|
1109
1129
|
ab_imag_vec = _mm256_castsi256_ps(_mm256_xor_si256(_mm256_castps_si256(ab_imag_vec), sign_flip_vec));
|
|
1110
1130
|
|
|
1111
1131
|
// Reduce horizontal sums and aggregate with the tail:
|
|
1112
|
-
simsimd_dot_f16c_serial(
|
|
1132
|
+
simsimd_dot_f16c_serial(a_pairs, b_pairs, count_pairs, results);
|
|
1113
1133
|
results[0] += _simsimd_reduce_f32x8_haswell(ab_real_vec);
|
|
1114
1134
|
results[1] += _simsimd_reduce_f32x8_haswell(ab_imag_vec);
|
|
1115
1135
|
}
|
|
1116
1136
|
|
|
1117
|
-
SIMSIMD_PUBLIC void simsimd_dot_i8_haswell(simsimd_i8_t const *
|
|
1118
|
-
simsimd_distance_t *result) {
|
|
1137
|
+
SIMSIMD_PUBLIC void simsimd_dot_i8_haswell(simsimd_i8_t const *a_scalars, simsimd_i8_t const *b_scalars,
|
|
1138
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
1119
1139
|
|
|
1120
1140
|
__m256i ab_i32_low_vec = _mm256_setzero_si256();
|
|
1121
1141
|
__m256i ab_i32_high_vec = _mm256_setzero_si256();
|
|
@@ -1132,10 +1152,10 @@ SIMSIMD_PUBLIC void simsimd_dot_i8_haswell(simsimd_i8_t const *a, simsimd_i8_t c
|
|
|
1132
1152
|
// The problem with this approach, however, is the `-128` value in the second vector.
|
|
1133
1153
|
// Flipping it's sign will do nothing, and the result will be incorrect.
|
|
1134
1154
|
// This can easily lead to noticeable numerical errors in the final result.
|
|
1135
|
-
simsimd_size_t
|
|
1136
|
-
for (;
|
|
1137
|
-
__m256i a_i8_vec = _mm256_lddqu_si256((__m256i const *)(
|
|
1138
|
-
__m256i b_i8_vec = _mm256_lddqu_si256((__m256i const *)(
|
|
1155
|
+
simsimd_size_t idx_scalars = 0;
|
|
1156
|
+
for (; idx_scalars + 32 <= count_scalars; idx_scalars += 32) {
|
|
1157
|
+
__m256i a_i8_vec = _mm256_lddqu_si256((__m256i const *)(a_scalars + idx_scalars));
|
|
1158
|
+
__m256i b_i8_vec = _mm256_lddqu_si256((__m256i const *)(b_scalars + idx_scalars));
|
|
1139
1159
|
|
|
1140
1160
|
// Upcast `int8` to `int16`
|
|
1141
1161
|
__m256i a_i16_low_vec = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(a_i8_vec, 0));
|
|
@@ -1152,12 +1172,12 @@ SIMSIMD_PUBLIC void simsimd_dot_i8_haswell(simsimd_i8_t const *a, simsimd_i8_t c
|
|
|
1152
1172
|
int ab = _simsimd_reduce_i32x8_haswell(_mm256_add_epi32(ab_i32_low_vec, ab_i32_high_vec));
|
|
1153
1173
|
|
|
1154
1174
|
// Take care of the tail:
|
|
1155
|
-
for (;
|
|
1175
|
+
for (; idx_scalars < count_scalars; ++idx_scalars) ab += (int)(a_scalars[idx_scalars]) * b_scalars[idx_scalars];
|
|
1156
1176
|
*result = ab;
|
|
1157
1177
|
}
|
|
1158
1178
|
|
|
1159
|
-
SIMSIMD_PUBLIC void simsimd_dot_u8_haswell(simsimd_u8_t const *
|
|
1160
|
-
simsimd_distance_t *result) {
|
|
1179
|
+
SIMSIMD_PUBLIC void simsimd_dot_u8_haswell(simsimd_u8_t const *a_scalars, simsimd_u8_t const *b_scalars,
|
|
1180
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
1161
1181
|
|
|
1162
1182
|
__m256i ab_i32_low_vec = _mm256_setzero_si256();
|
|
1163
1183
|
__m256i ab_i32_high_vec = _mm256_setzero_si256();
|
|
@@ -1165,10 +1185,10 @@ SIMSIMD_PUBLIC void simsimd_dot_u8_haswell(simsimd_u8_t const *a, simsimd_u8_t c
|
|
|
1165
1185
|
|
|
1166
1186
|
// AVX2 has no instructions for unsigned 8-bit integer dot-products,
|
|
1167
1187
|
// but it has a weird instruction for mixed signed-unsigned 8-bit dot-product.
|
|
1168
|
-
simsimd_size_t
|
|
1169
|
-
for (;
|
|
1170
|
-
__m256i a_u8_vec = _mm256_lddqu_si256((__m256i const *)(
|
|
1171
|
-
__m256i b_u8_vec = _mm256_lddqu_si256((__m256i const *)(
|
|
1188
|
+
simsimd_size_t idx_scalars = 0;
|
|
1189
|
+
for (; idx_scalars + 32 <= count_scalars; idx_scalars += 32) {
|
|
1190
|
+
__m256i a_u8_vec = _mm256_lddqu_si256((__m256i const *)(a_scalars + idx_scalars));
|
|
1191
|
+
__m256i b_u8_vec = _mm256_lddqu_si256((__m256i const *)(b_scalars + idx_scalars));
|
|
1172
1192
|
|
|
1173
1193
|
// Upcast `uint8` to `int16`. Unlike the signed version, we can use the unpacking
|
|
1174
1194
|
// instructions instead of extracts, as they are much faster and more efficient.
|
|
@@ -1186,22 +1206,22 @@ SIMSIMD_PUBLIC void simsimd_dot_u8_haswell(simsimd_u8_t const *a, simsimd_u8_t c
|
|
|
1186
1206
|
int ab = _simsimd_reduce_i32x8_haswell(_mm256_add_epi32(ab_i32_low_vec, ab_i32_high_vec));
|
|
1187
1207
|
|
|
1188
1208
|
// Take care of the tail:
|
|
1189
|
-
for (;
|
|
1209
|
+
for (; idx_scalars < count_scalars; ++idx_scalars) ab += (int)(a_scalars[idx_scalars]) * b_scalars[idx_scalars];
|
|
1190
1210
|
*result = ab;
|
|
1191
1211
|
}
|
|
1192
1212
|
|
|
1193
|
-
SIMSIMD_INTERNAL __m256 _simsimd_bf16x8_to_f32x8_haswell(__m128i
|
|
1213
|
+
SIMSIMD_INTERNAL __m256 _simsimd_bf16x8_to_f32x8_haswell(__m128i x) {
|
|
1194
1214
|
// Upcasting from `bf16` to `f32` is done by shifting the `bf16` values by 16 bits to the left, like:
|
|
1195
|
-
return _mm256_castsi256_ps(_mm256_slli_epi32(_mm256_cvtepu16_epi32(
|
|
1215
|
+
return _mm256_castsi256_ps(_mm256_slli_epi32(_mm256_cvtepu16_epi32(x), 16));
|
|
1196
1216
|
}
|
|
1197
1217
|
|
|
1198
|
-
SIMSIMD_INTERNAL __m128i _simsimd_f32x8_to_bf16x8_haswell(__m256
|
|
1218
|
+
SIMSIMD_INTERNAL __m128i _simsimd_f32x8_to_bf16x8_haswell(__m256 x) {
|
|
1199
1219
|
// Pack the 32-bit integers into 16-bit integers.
|
|
1200
1220
|
// This is less trivial than unpacking: https://stackoverflow.com/a/77781241/2766161
|
|
1201
1221
|
// The best approach is to shuffle within lanes first: https://stackoverflow.com/a/49723746/2766161
|
|
1202
1222
|
// Our shuffling mask will drop the low 2-bytes from every 4-byte word.
|
|
1203
1223
|
__m256i trunc_elements = _mm256_shuffle_epi8( //
|
|
1204
|
-
_mm256_castps_si256(
|
|
1224
|
+
_mm256_castps_si256(x), //
|
|
1205
1225
|
_mm256_set_epi8( //
|
|
1206
1226
|
-1, -1, -1, -1, -1, -1, -1, -1, 15, 14, 11, 10, 7, 6, 3, 2, //
|
|
1207
1227
|
-1, -1, -1, -1, -1, -1, -1, -1, 15, 14, 11, 10, 7, 6, 3, 2 //
|
|
@@ -1225,24 +1245,24 @@ SIMSIMD_INTERNAL __m128i _simsimd_partial_load_bf16x8_haswell(simsimd_bf16_t con
|
|
|
1225
1245
|
return result.vec;
|
|
1226
1246
|
}
|
|
1227
1247
|
|
|
1228
|
-
SIMSIMD_PUBLIC void simsimd_dot_bf16_haswell(simsimd_bf16_t const *
|
|
1229
|
-
simsimd_distance_t *result) {
|
|
1248
|
+
SIMSIMD_PUBLIC void simsimd_dot_bf16_haswell(simsimd_bf16_t const *a_scalars, simsimd_bf16_t const *b_scalars,
|
|
1249
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
1230
1250
|
__m128i a_vec, b_vec;
|
|
1231
1251
|
__m256 ab_vec = _mm256_setzero_ps();
|
|
1232
1252
|
|
|
1233
1253
|
simsimd_dot_bf16_haswell_cycle:
|
|
1234
|
-
if (
|
|
1235
|
-
a_vec = _simsimd_partial_load_bf16x8_haswell(
|
|
1236
|
-
b_vec = _simsimd_partial_load_bf16x8_haswell(
|
|
1237
|
-
|
|
1254
|
+
if (count_scalars < 8) {
|
|
1255
|
+
a_vec = _simsimd_partial_load_bf16x8_haswell(a_scalars, count_scalars);
|
|
1256
|
+
b_vec = _simsimd_partial_load_bf16x8_haswell(b_scalars, count_scalars);
|
|
1257
|
+
count_scalars = 0;
|
|
1238
1258
|
}
|
|
1239
1259
|
else {
|
|
1240
|
-
a_vec = _mm_lddqu_si128((__m128i const *)
|
|
1241
|
-
b_vec = _mm_lddqu_si128((__m128i const *)
|
|
1242
|
-
|
|
1260
|
+
a_vec = _mm_lddqu_si128((__m128i const *)a_scalars);
|
|
1261
|
+
b_vec = _mm_lddqu_si128((__m128i const *)b_scalars);
|
|
1262
|
+
a_scalars += 8, b_scalars += 8, count_scalars -= 8;
|
|
1243
1263
|
}
|
|
1244
1264
|
ab_vec = _mm256_fmadd_ps(_simsimd_bf16x8_to_f32x8_haswell(a_vec), _simsimd_bf16x8_to_f32x8_haswell(b_vec), ab_vec);
|
|
1245
|
-
if (
|
|
1265
|
+
if (count_scalars) goto simsimd_dot_bf16_haswell_cycle;
|
|
1246
1266
|
|
|
1247
1267
|
*result = _simsimd_reduce_f32x8_haswell(ab_vec);
|
|
1248
1268
|
}
|
|
@@ -1274,87 +1294,80 @@ SIMSIMD_INTERNAL __m256i _simsimd_f32x16_to_bf16x16_skylake(__m512 a) {
|
|
|
1274
1294
|
return _mm512_cvtepi32_epi16(x);
|
|
1275
1295
|
}
|
|
1276
1296
|
|
|
1277
|
-
SIMSIMD_PUBLIC void simsimd_dot_f32_skylake(simsimd_f32_t const *
|
|
1278
|
-
simsimd_distance_t *result) {
|
|
1279
|
-
__m512 ab_vec = _mm512_setzero();
|
|
1297
|
+
SIMSIMD_PUBLIC void simsimd_dot_f32_skylake(simsimd_f32_t const *a_scalars, simsimd_f32_t const *b_scalars,
|
|
1298
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
1280
1299
|
__m512 a_vec, b_vec;
|
|
1300
|
+
__m512 ab_vec = _mm512_setzero();
|
|
1281
1301
|
|
|
1282
1302
|
simsimd_dot_f32_skylake_cycle:
|
|
1283
|
-
if (
|
|
1284
|
-
__mmask16 mask = (__mmask16)_bzhi_u32(0xFFFFFFFF,
|
|
1285
|
-
a_vec = _mm512_maskz_loadu_ps(mask,
|
|
1286
|
-
b_vec = _mm512_maskz_loadu_ps(mask,
|
|
1287
|
-
|
|
1303
|
+
if (count_scalars < 16) {
|
|
1304
|
+
__mmask16 mask = (__mmask16)_bzhi_u32(0xFFFFFFFF, count_scalars);
|
|
1305
|
+
a_vec = _mm512_maskz_loadu_ps(mask, a_scalars);
|
|
1306
|
+
b_vec = _mm512_maskz_loadu_ps(mask, b_scalars);
|
|
1307
|
+
count_scalars = 0;
|
|
1288
1308
|
}
|
|
1289
1309
|
else {
|
|
1290
|
-
a_vec = _mm512_loadu_ps(
|
|
1291
|
-
b_vec = _mm512_loadu_ps(
|
|
1292
|
-
|
|
1310
|
+
a_vec = _mm512_loadu_ps(a_scalars);
|
|
1311
|
+
b_vec = _mm512_loadu_ps(b_scalars);
|
|
1312
|
+
a_scalars += 16, b_scalars += 16, count_scalars -= 16;
|
|
1293
1313
|
}
|
|
1294
1314
|
ab_vec = _mm512_fmadd_ps(a_vec, b_vec, ab_vec);
|
|
1295
|
-
if (
|
|
1315
|
+
if (count_scalars) goto simsimd_dot_f32_skylake_cycle;
|
|
1296
1316
|
|
|
1297
1317
|
*result = _simsimd_reduce_f32x16_skylake(ab_vec);
|
|
1298
1318
|
}
|
|
1299
1319
|
|
|
1300
|
-
SIMSIMD_PUBLIC void simsimd_dot_f64_skylake(simsimd_f64_t const *
|
|
1301
|
-
simsimd_distance_t *result) {
|
|
1302
|
-
__m512d ab_vec = _mm512_setzero_pd();
|
|
1320
|
+
SIMSIMD_PUBLIC void simsimd_dot_f64_skylake(simsimd_f64_t const *a_scalars, simsimd_f64_t const *b_scalars,
|
|
1321
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
1303
1322
|
__m512d a_vec, b_vec;
|
|
1323
|
+
__m512d ab_vec = _mm512_setzero_pd();
|
|
1304
1324
|
|
|
1305
1325
|
simsimd_dot_f64_skylake_cycle:
|
|
1306
|
-
if (
|
|
1307
|
-
__mmask8 mask = (__mmask8)_bzhi_u32(0xFFFFFFFF,
|
|
1308
|
-
a_vec = _mm512_maskz_loadu_pd(mask,
|
|
1309
|
-
b_vec = _mm512_maskz_loadu_pd(mask,
|
|
1310
|
-
|
|
1326
|
+
if (count_scalars < 8) {
|
|
1327
|
+
__mmask8 mask = (__mmask8)_bzhi_u32(0xFFFFFFFF, count_scalars);
|
|
1328
|
+
a_vec = _mm512_maskz_loadu_pd(mask, a_scalars);
|
|
1329
|
+
b_vec = _mm512_maskz_loadu_pd(mask, b_scalars);
|
|
1330
|
+
count_scalars = 0;
|
|
1311
1331
|
}
|
|
1312
1332
|
else {
|
|
1313
|
-
a_vec = _mm512_loadu_pd(
|
|
1314
|
-
b_vec = _mm512_loadu_pd(
|
|
1315
|
-
|
|
1333
|
+
a_vec = _mm512_loadu_pd(a_scalars);
|
|
1334
|
+
b_vec = _mm512_loadu_pd(b_scalars);
|
|
1335
|
+
a_scalars += 8, b_scalars += 8, count_scalars -= 8;
|
|
1316
1336
|
}
|
|
1317
1337
|
ab_vec = _mm512_fmadd_pd(a_vec, b_vec, ab_vec);
|
|
1318
|
-
if (
|
|
1338
|
+
if (count_scalars) goto simsimd_dot_f64_skylake_cycle;
|
|
1319
1339
|
|
|
1320
1340
|
*result = _mm512_reduce_add_pd(ab_vec);
|
|
1321
1341
|
}
|
|
1322
1342
|
|
|
1323
|
-
SIMSIMD_PUBLIC void simsimd_dot_f32c_skylake(
|
|
1324
|
-
simsimd_distance_t *results) {
|
|
1343
|
+
SIMSIMD_PUBLIC void simsimd_dot_f32c_skylake(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
|
|
1344
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
1345
|
+
__m512 a_vec, b_vec;
|
|
1325
1346
|
__m512 ab_real_vec = _mm512_setzero();
|
|
1326
1347
|
__m512 ab_imag_vec = _mm512_setzero();
|
|
1327
|
-
__m512 a_vec;
|
|
1328
|
-
__m512 b_vec;
|
|
1329
1348
|
|
|
1330
1349
|
// We take into account, that FMS is the same as FMA with a negative multiplier.
|
|
1331
1350
|
// To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
|
|
1332
1351
|
// This way we can avoid the shuffling and the need for separate real and imaginary parts.
|
|
1333
1352
|
// For the imaginary part of the product, we would need to swap the real and imaginary parts of
|
|
1334
1353
|
// one of the vectors.
|
|
1335
|
-
__m512i sign_flip_vec = _mm512_set1_epi64(0x8000000000000000);
|
|
1336
|
-
__m512i swap_adjacent_vec = _mm512_set_epi8( //
|
|
1337
|
-
59, 58, 57, 56, 63, 62, 61, 60, 51, 50, 49, 48, 55, 54, 53, 52, // 4th 128-bit lane
|
|
1338
|
-
43, 42, 41, 40, 47, 46, 45, 44, 35, 34, 33, 32, 39, 38, 37, 36, // 3rd 128-bit lane
|
|
1339
|
-
27, 26, 25, 24, 31, 30, 29, 28, 19, 18, 17, 16, 23, 22, 21, 20, // 2nd 128-bit lane
|
|
1340
|
-
11, 10, 9, 8, 15, 14, 13, 12, 3, 2, 1, 0, 7, 6, 5, 4 // 1st 128-bit lane
|
|
1341
|
-
);
|
|
1354
|
+
__m512i const sign_flip_vec = _mm512_set1_epi64(0x8000000000000000);
|
|
1342
1355
|
simsimd_dot_f32c_skylake_cycle:
|
|
1343
|
-
if (
|
|
1344
|
-
__mmask16 mask = (__mmask16)_bzhi_u32(0xFFFFFFFF,
|
|
1345
|
-
a_vec = _mm512_maskz_loadu_ps(mask,
|
|
1346
|
-
b_vec = _mm512_maskz_loadu_ps(mask,
|
|
1347
|
-
|
|
1356
|
+
if (count_pairs < 8) {
|
|
1357
|
+
__mmask16 mask = (__mmask16)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
|
|
1358
|
+
a_vec = _mm512_maskz_loadu_ps(mask, a_pairs);
|
|
1359
|
+
b_vec = _mm512_maskz_loadu_ps(mask, b_pairs);
|
|
1360
|
+
count_pairs = 0;
|
|
1348
1361
|
}
|
|
1349
1362
|
else {
|
|
1350
|
-
a_vec = _mm512_loadu_ps(
|
|
1351
|
-
b_vec = _mm512_loadu_ps(
|
|
1352
|
-
|
|
1363
|
+
a_vec = _mm512_loadu_ps(a_pairs);
|
|
1364
|
+
b_vec = _mm512_loadu_ps(b_pairs);
|
|
1365
|
+
a_pairs += 8, b_pairs += 8, count_pairs -= 8;
|
|
1353
1366
|
}
|
|
1354
1367
|
ab_real_vec = _mm512_fmadd_ps(b_vec, a_vec, ab_real_vec);
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
if (
|
|
1368
|
+
b_vec = _mm512_permute_ps(b_vec, 0xB1); //? Swap adjacent entries within each pair
|
|
1369
|
+
ab_imag_vec = _mm512_fmadd_ps(b_vec, a_vec, ab_imag_vec);
|
|
1370
|
+
if (count_pairs) goto simsimd_dot_f32c_skylake_cycle;
|
|
1358
1371
|
|
|
1359
1372
|
// Flip the sign bit in every second scalar before accumulation:
|
|
1360
1373
|
ab_real_vec = _mm512_castsi512_ps(_mm512_xor_si512(_mm512_castps_si512(ab_real_vec), sign_flip_vec));
|
|
@@ -1364,41 +1377,40 @@ simsimd_dot_f32c_skylake_cycle:
|
|
|
1364
1377
|
results[1] = _simsimd_reduce_f32x16_skylake(ab_imag_vec);
|
|
1365
1378
|
}
|
|
1366
1379
|
|
|
1367
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f32c_skylake(
|
|
1368
|
-
simsimd_distance_t *results) {
|
|
1380
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f32c_skylake(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
|
|
1381
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
1382
|
+
__m512 a_vec, b_vec;
|
|
1369
1383
|
__m512 ab_real_vec = _mm512_setzero();
|
|
1370
1384
|
__m512 ab_imag_vec = _mm512_setzero();
|
|
1371
|
-
__m512 a_vec;
|
|
1372
|
-
__m512 b_vec;
|
|
1373
1385
|
|
|
1374
1386
|
// We take into account, that FMS is the same as FMA with a negative multiplier.
|
|
1375
1387
|
// To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
|
|
1376
1388
|
// This way we can avoid the shuffling and the need for separate real and imaginary parts.
|
|
1377
1389
|
// For the imaginary part of the product, we would need to swap the real and imaginary parts of
|
|
1378
1390
|
// one of the vectors.
|
|
1379
|
-
__m512i sign_flip_vec = _mm512_set1_epi64(0x8000000000000000);
|
|
1380
|
-
__m512i swap_adjacent_vec = _mm512_set_epi8(
|
|
1391
|
+
__m512i const sign_flip_vec = _mm512_set1_epi64(0x8000000000000000);
|
|
1392
|
+
__m512i const swap_adjacent_vec = _mm512_set_epi8( //
|
|
1381
1393
|
59, 58, 57, 56, 63, 62, 61, 60, 51, 50, 49, 48, 55, 54, 53, 52, // 4th 128-bit lane
|
|
1382
1394
|
43, 42, 41, 40, 47, 46, 45, 44, 35, 34, 33, 32, 39, 38, 37, 36, // 3rd 128-bit lane
|
|
1383
1395
|
27, 26, 25, 24, 31, 30, 29, 28, 19, 18, 17, 16, 23, 22, 21, 20, // 2nd 128-bit lane
|
|
1384
1396
|
11, 10, 9, 8, 15, 14, 13, 12, 3, 2, 1, 0, 7, 6, 5, 4 // 1st 128-bit lane
|
|
1385
1397
|
);
|
|
1386
1398
|
simsimd_vdot_f32c_skylake_cycle:
|
|
1387
|
-
if (
|
|
1388
|
-
__mmask16 mask = (__mmask16)_bzhi_u32(0xFFFFFFFF,
|
|
1389
|
-
a_vec = _mm512_maskz_loadu_ps(mask,
|
|
1390
|
-
b_vec = _mm512_maskz_loadu_ps(mask,
|
|
1391
|
-
|
|
1399
|
+
if (count_pairs < 8) {
|
|
1400
|
+
__mmask16 mask = (__mmask16)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
|
|
1401
|
+
a_vec = _mm512_maskz_loadu_ps(mask, (simsimd_f32_t const *)a_pairs);
|
|
1402
|
+
b_vec = _mm512_maskz_loadu_ps(mask, (simsimd_f32_t const *)b_pairs);
|
|
1403
|
+
count_pairs = 0;
|
|
1392
1404
|
}
|
|
1393
1405
|
else {
|
|
1394
|
-
a_vec = _mm512_loadu_ps(
|
|
1395
|
-
b_vec = _mm512_loadu_ps(
|
|
1396
|
-
|
|
1406
|
+
a_vec = _mm512_loadu_ps((simsimd_f32_t const *)a_pairs);
|
|
1407
|
+
b_vec = _mm512_loadu_ps((simsimd_f32_t const *)b_pairs);
|
|
1408
|
+
a_pairs += 8, b_pairs += 8, count_pairs -= 8;
|
|
1397
1409
|
}
|
|
1398
1410
|
ab_real_vec = _mm512_fmadd_ps(a_vec, b_vec, ab_real_vec);
|
|
1399
|
-
b_vec =
|
|
1411
|
+
b_vec = _mm512_permute_ps(b_vec, 0xB1); //? Swap adjacent entries within each pair
|
|
1400
1412
|
ab_imag_vec = _mm512_fmadd_ps(a_vec, b_vec, ab_imag_vec);
|
|
1401
|
-
if (
|
|
1413
|
+
if (count_pairs) goto simsimd_vdot_f32c_skylake_cycle;
|
|
1402
1414
|
|
|
1403
1415
|
// Flip the sign bit in every second scalar before accumulation:
|
|
1404
1416
|
ab_imag_vec = _mm512_castsi512_ps(_mm512_xor_si512(_mm512_castps_si512(ab_imag_vec), sign_flip_vec));
|
|
@@ -1408,44 +1420,37 @@ simsimd_vdot_f32c_skylake_cycle:
|
|
|
1408
1420
|
results[1] = _simsimd_reduce_f32x16_skylake(ab_imag_vec);
|
|
1409
1421
|
}
|
|
1410
1422
|
|
|
1411
|
-
SIMSIMD_PUBLIC void simsimd_dot_f64c_skylake(
|
|
1412
|
-
simsimd_distance_t *results) {
|
|
1423
|
+
SIMSIMD_PUBLIC void simsimd_dot_f64c_skylake(simsimd_f64c_t const *a_pairs, simsimd_f64c_t const *b_pairs,
|
|
1424
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
1425
|
+
__m512d a_vec, b_vec;
|
|
1413
1426
|
__m512d ab_real_vec = _mm512_setzero_pd();
|
|
1414
1427
|
__m512d ab_imag_vec = _mm512_setzero_pd();
|
|
1415
|
-
__m512d a_vec;
|
|
1416
|
-
__m512d b_vec;
|
|
1417
1428
|
|
|
1418
1429
|
// We take into account, that FMS is the same as FMA with a negative multiplier.
|
|
1419
1430
|
// To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
|
|
1420
1431
|
// This way we can avoid the shuffling and the need for separate real and imaginary parts.
|
|
1421
1432
|
// For the imaginary part of the product, we would need to swap the real and imaginary parts of
|
|
1422
1433
|
// one of the vectors.
|
|
1423
|
-
__m512i sign_flip_vec = _mm512_set_epi64(
|
|
1434
|
+
__m512i const sign_flip_vec = _mm512_set_epi64( //
|
|
1424
1435
|
0x8000000000000000, 0x0000000000000000, 0x8000000000000000, 0x0000000000000000, //
|
|
1425
1436
|
0x8000000000000000, 0x0000000000000000, 0x8000000000000000, 0x0000000000000000 //
|
|
1426
1437
|
);
|
|
1427
|
-
__m512i swap_adjacent_vec = _mm512_set_epi8( //
|
|
1428
|
-
55, 54, 53, 52, 51, 50, 49, 48, 63, 62, 61, 60, 59, 58, 57, 56, // 4th 128-bit lane
|
|
1429
|
-
39, 38, 37, 36, 35, 34, 33, 32, 47, 46, 45, 44, 43, 42, 41, 40, // 3rd 128-bit lane
|
|
1430
|
-
23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, // 2nd 128-bit lane
|
|
1431
|
-
7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8 // 1st 128-bit lane
|
|
1432
|
-
);
|
|
1433
1438
|
simsimd_dot_f64c_skylake_cycle:
|
|
1434
|
-
if (
|
|
1435
|
-
__mmask8 mask = (__mmask8)_bzhi_u32(0xFFFFFFFF,
|
|
1436
|
-
a_vec = _mm512_maskz_loadu_pd(mask,
|
|
1437
|
-
b_vec = _mm512_maskz_loadu_pd(mask,
|
|
1438
|
-
|
|
1439
|
+
if (count_pairs < 4) {
|
|
1440
|
+
__mmask8 mask = (__mmask8)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
|
|
1441
|
+
a_vec = _mm512_maskz_loadu_pd(mask, a_pairs);
|
|
1442
|
+
b_vec = _mm512_maskz_loadu_pd(mask, b_pairs);
|
|
1443
|
+
count_pairs = 0;
|
|
1439
1444
|
}
|
|
1440
1445
|
else {
|
|
1441
|
-
a_vec = _mm512_loadu_pd(
|
|
1442
|
-
b_vec = _mm512_loadu_pd(
|
|
1443
|
-
|
|
1446
|
+
a_vec = _mm512_loadu_pd(a_pairs);
|
|
1447
|
+
b_vec = _mm512_loadu_pd(b_pairs);
|
|
1448
|
+
a_pairs += 4, b_pairs += 4, count_pairs -= 4;
|
|
1444
1449
|
}
|
|
1445
1450
|
ab_real_vec = _mm512_fmadd_pd(b_vec, a_vec, ab_real_vec);
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
if (
|
|
1451
|
+
b_vec = _mm512_permute_pd(b_vec, 0x55); //? Same as 0b01010101.
|
|
1452
|
+
ab_imag_vec = _mm512_fmadd_pd(b_vec, a_vec, ab_imag_vec);
|
|
1453
|
+
if (count_pairs) goto simsimd_dot_f64c_skylake_cycle;
|
|
1449
1454
|
|
|
1450
1455
|
// Flip the sign bit in every second scalar before accumulation:
|
|
1451
1456
|
ab_real_vec = _mm512_castsi512_pd(_mm512_xor_si512(_mm512_castpd_si512(ab_real_vec), sign_flip_vec));
|
|
@@ -1455,44 +1460,37 @@ simsimd_dot_f64c_skylake_cycle:
|
|
|
1455
1460
|
results[1] = _mm512_reduce_add_pd(ab_imag_vec);
|
|
1456
1461
|
}
|
|
1457
1462
|
|
|
1458
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f64c_skylake(
|
|
1459
|
-
simsimd_distance_t *results) {
|
|
1463
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f64c_skylake(simsimd_f64c_t const *a_pairs, simsimd_f64c_t const *b_pairs,
|
|
1464
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
1465
|
+
__m512d a_vec, b_vec;
|
|
1460
1466
|
__m512d ab_real_vec = _mm512_setzero_pd();
|
|
1461
1467
|
__m512d ab_imag_vec = _mm512_setzero_pd();
|
|
1462
|
-
__m512d a_vec;
|
|
1463
|
-
__m512d b_vec;
|
|
1464
1468
|
|
|
1465
1469
|
// We take into account, that FMS is the same as FMA with a negative multiplier.
|
|
1466
1470
|
// To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
|
|
1467
1471
|
// This way we can avoid the shuffling and the need for separate real and imaginary parts.
|
|
1468
1472
|
// For the imaginary part of the product, we would need to swap the real and imaginary parts of
|
|
1469
1473
|
// one of the vectors.
|
|
1470
|
-
__m512i sign_flip_vec = _mm512_set_epi64(
|
|
1474
|
+
__m512i const sign_flip_vec = _mm512_set_epi64( //
|
|
1471
1475
|
0x8000000000000000, 0x0000000000000000, 0x8000000000000000, 0x0000000000000000, //
|
|
1472
1476
|
0x8000000000000000, 0x0000000000000000, 0x8000000000000000, 0x0000000000000000 //
|
|
1473
1477
|
);
|
|
1474
|
-
__m512i swap_adjacent_vec = _mm512_set_epi8( //
|
|
1475
|
-
55, 54, 53, 52, 51, 50, 49, 48, 63, 62, 61, 60, 59, 58, 57, 56, // 4th 128-bit lane
|
|
1476
|
-
39, 38, 37, 36, 35, 34, 33, 32, 47, 46, 45, 44, 43, 42, 41, 40, // 3rd 128-bit lane
|
|
1477
|
-
23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, // 2nd 128-bit lane
|
|
1478
|
-
7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8 // 1st 128-bit lane
|
|
1479
|
-
);
|
|
1480
1478
|
simsimd_vdot_f64c_skylake_cycle:
|
|
1481
|
-
if (
|
|
1482
|
-
__mmask8 mask = (__mmask8)_bzhi_u32(0xFFFFFFFF,
|
|
1483
|
-
a_vec = _mm512_maskz_loadu_pd(mask,
|
|
1484
|
-
b_vec = _mm512_maskz_loadu_pd(mask,
|
|
1485
|
-
|
|
1479
|
+
if (count_pairs < 4) {
|
|
1480
|
+
__mmask8 mask = (__mmask8)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
|
|
1481
|
+
a_vec = _mm512_maskz_loadu_pd(mask, (simsimd_f32_t const *)a_pairs);
|
|
1482
|
+
b_vec = _mm512_maskz_loadu_pd(mask, (simsimd_f32_t const *)b_pairs);
|
|
1483
|
+
count_pairs = 0;
|
|
1486
1484
|
}
|
|
1487
1485
|
else {
|
|
1488
|
-
a_vec = _mm512_loadu_pd(
|
|
1489
|
-
b_vec = _mm512_loadu_pd(
|
|
1490
|
-
|
|
1486
|
+
a_vec = _mm512_loadu_pd((simsimd_f32_t const *)a_pairs);
|
|
1487
|
+
b_vec = _mm512_loadu_pd((simsimd_f32_t const *)b_pairs);
|
|
1488
|
+
a_pairs += 4, b_pairs += 4, count_pairs -= 4;
|
|
1491
1489
|
}
|
|
1492
1490
|
ab_real_vec = _mm512_fmadd_pd(a_vec, b_vec, ab_real_vec);
|
|
1493
|
-
b_vec =
|
|
1491
|
+
b_vec = _mm512_permute_pd(b_vec, 0x55); //? Same as 0b01010101.
|
|
1494
1492
|
ab_imag_vec = _mm512_fmadd_pd(a_vec, b_vec, ab_imag_vec);
|
|
1495
|
-
if (
|
|
1493
|
+
if (count_pairs) goto simsimd_vdot_f64c_skylake_cycle;
|
|
1496
1494
|
|
|
1497
1495
|
// Flip the sign bit in every second scalar before accumulation:
|
|
1498
1496
|
ab_imag_vec = _mm512_castsi512_pd(_mm512_xor_si512(_mm512_castpd_si512(ab_imag_vec), sign_flip_vec));
|
|
@@ -1512,43 +1510,42 @@ simsimd_vdot_f64c_skylake_cycle:
|
|
|
1512
1510
|
#pragma clang attribute push(__attribute__((target("avx2,avx512f,avx512vl,bmi2,avx512bw,avx512bf16"))), \
|
|
1513
1511
|
apply_to = function)
|
|
1514
1512
|
|
|
1515
|
-
SIMSIMD_PUBLIC void simsimd_dot_bf16_genoa(simsimd_bf16_t const *
|
|
1516
|
-
simsimd_distance_t *result) {
|
|
1517
|
-
__m512 ab_vec = _mm512_setzero_ps();
|
|
1513
|
+
SIMSIMD_PUBLIC void simsimd_dot_bf16_genoa(simsimd_bf16_t const *a_scalars, simsimd_bf16_t const *b_scalars,
|
|
1514
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
1518
1515
|
__m512i a_i16_vec, b_i16_vec;
|
|
1516
|
+
__m512 ab_vec = _mm512_setzero_ps();
|
|
1519
1517
|
|
|
1520
1518
|
simsimd_dot_bf16_genoa_cycle:
|
|
1521
|
-
if (
|
|
1522
|
-
__mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF,
|
|
1523
|
-
a_i16_vec = _mm512_maskz_loadu_epi16(mask,
|
|
1524
|
-
b_i16_vec = _mm512_maskz_loadu_epi16(mask,
|
|
1525
|
-
|
|
1519
|
+
if (count_scalars < 32) {
|
|
1520
|
+
__mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, count_scalars);
|
|
1521
|
+
a_i16_vec = _mm512_maskz_loadu_epi16(mask, a_scalars);
|
|
1522
|
+
b_i16_vec = _mm512_maskz_loadu_epi16(mask, b_scalars);
|
|
1523
|
+
count_scalars = 0;
|
|
1526
1524
|
}
|
|
1527
1525
|
else {
|
|
1528
|
-
a_i16_vec = _mm512_loadu_epi16(
|
|
1529
|
-
b_i16_vec = _mm512_loadu_epi16(
|
|
1530
|
-
|
|
1526
|
+
a_i16_vec = _mm512_loadu_epi16(a_scalars);
|
|
1527
|
+
b_i16_vec = _mm512_loadu_epi16(b_scalars);
|
|
1528
|
+
a_scalars += 32, b_scalars += 32, count_scalars -= 32;
|
|
1531
1529
|
}
|
|
1532
1530
|
ab_vec = _mm512_dpbf16_ps(ab_vec, (__m512bh)(a_i16_vec), (__m512bh)(b_i16_vec));
|
|
1533
|
-
if (
|
|
1531
|
+
if (count_scalars) goto simsimd_dot_bf16_genoa_cycle;
|
|
1534
1532
|
|
|
1535
1533
|
*result = _simsimd_reduce_f32x16_skylake(ab_vec);
|
|
1536
1534
|
}
|
|
1537
1535
|
|
|
1538
|
-
SIMSIMD_PUBLIC void simsimd_dot_bf16c_genoa(
|
|
1539
|
-
simsimd_distance_t *results) {
|
|
1536
|
+
SIMSIMD_PUBLIC void simsimd_dot_bf16c_genoa(simsimd_bf16c_t const *a_pairs, simsimd_bf16c_t const *b_pairs,
|
|
1537
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
1538
|
+
__m512i a_vec, b_vec;
|
|
1540
1539
|
__m512 ab_real_vec = _mm512_setzero_ps();
|
|
1541
1540
|
__m512 ab_imag_vec = _mm512_setzero_ps();
|
|
1542
|
-
__m512i a_vec;
|
|
1543
|
-
__m512i b_vec;
|
|
1544
1541
|
|
|
1545
1542
|
// We take into account, that FMS is the same as FMA with a negative multiplier.
|
|
1546
1543
|
// To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
|
|
1547
1544
|
// This way we can avoid the shuffling and the need for separate real and imaginary parts.
|
|
1548
1545
|
// For the imaginary part of the product, we would need to swap the real and imaginary parts of
|
|
1549
1546
|
// one of the vectors.
|
|
1550
|
-
__m512i sign_flip_vec = _mm512_set1_epi32(0x80000000);
|
|
1551
|
-
__m512i swap_adjacent_vec = _mm512_set_epi8(
|
|
1547
|
+
__m512i const sign_flip_vec = _mm512_set1_epi32(0x80000000);
|
|
1548
|
+
__m512i const swap_adjacent_vec = _mm512_set_epi8( //
|
|
1552
1549
|
61, 60, 63, 62, 57, 56, 59, 58, 53, 52, 55, 54, 49, 48, 51, 50, // 4th 128-bit lane
|
|
1553
1550
|
45, 44, 47, 46, 41, 40, 43, 42, 37, 36, 39, 38, 33, 32, 35, 34, // 3rd 128-bit lane
|
|
1554
1551
|
29, 28, 31, 30, 25, 24, 27, 26, 21, 20, 23, 22, 17, 16, 19, 18, // 2nd 128-bit lane
|
|
@@ -1556,41 +1553,40 @@ SIMSIMD_PUBLIC void simsimd_dot_bf16c_genoa(simsimd_bf16_t const *a, simsimd_bf1
|
|
|
1556
1553
|
);
|
|
1557
1554
|
|
|
1558
1555
|
simsimd_dot_bf16c_genoa_cycle:
|
|
1559
|
-
if (
|
|
1560
|
-
__mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF,
|
|
1561
|
-
a_vec = _mm512_maskz_loadu_epi16(mask,
|
|
1562
|
-
b_vec = _mm512_maskz_loadu_epi16(mask,
|
|
1563
|
-
|
|
1556
|
+
if (count_pairs < 16) {
|
|
1557
|
+
__mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
|
|
1558
|
+
a_vec = _mm512_maskz_loadu_epi16(mask, (simsimd_i16_t const *)a_pairs);
|
|
1559
|
+
b_vec = _mm512_maskz_loadu_epi16(mask, (simsimd_i16_t const *)b_pairs);
|
|
1560
|
+
count_pairs = 0;
|
|
1564
1561
|
}
|
|
1565
1562
|
else {
|
|
1566
|
-
a_vec = _mm512_loadu_epi16(
|
|
1567
|
-
b_vec = _mm512_loadu_epi16(
|
|
1568
|
-
|
|
1563
|
+
a_vec = _mm512_loadu_epi16((simsimd_i16_t const *)a_pairs);
|
|
1564
|
+
b_vec = _mm512_loadu_epi16((simsimd_i16_t const *)b_pairs);
|
|
1565
|
+
a_pairs += 16, b_pairs += 16, count_pairs -= 16;
|
|
1569
1566
|
}
|
|
1570
1567
|
ab_real_vec = _mm512_dpbf16_ps(ab_real_vec, (__m512bh)(_mm512_xor_si512(b_vec, sign_flip_vec)), (__m512bh)(a_vec));
|
|
1571
1568
|
ab_imag_vec =
|
|
1572
1569
|
_mm512_dpbf16_ps(ab_imag_vec, (__m512bh)(_mm512_shuffle_epi8(b_vec, swap_adjacent_vec)), (__m512bh)(a_vec));
|
|
1573
|
-
if (
|
|
1570
|
+
if (count_pairs) goto simsimd_dot_bf16c_genoa_cycle;
|
|
1574
1571
|
|
|
1575
1572
|
// Reduce horizontal sums:
|
|
1576
1573
|
results[0] = _simsimd_reduce_f32x16_skylake(ab_real_vec);
|
|
1577
1574
|
results[1] = _simsimd_reduce_f32x16_skylake(ab_imag_vec);
|
|
1578
1575
|
}
|
|
1579
1576
|
|
|
1580
|
-
SIMSIMD_PUBLIC void simsimd_vdot_bf16c_genoa(
|
|
1581
|
-
simsimd_distance_t *results) {
|
|
1577
|
+
SIMSIMD_PUBLIC void simsimd_vdot_bf16c_genoa(simsimd_bf16c_t const *a_pairs, simsimd_bf16c_t const *b_pairs,
|
|
1578
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
1579
|
+
__m512i a_vec, b_vec;
|
|
1582
1580
|
__m512 ab_real_vec = _mm512_setzero_ps();
|
|
1583
1581
|
__m512 ab_imag_vec = _mm512_setzero_ps();
|
|
1584
|
-
__m512i a_vec;
|
|
1585
|
-
__m512i b_vec;
|
|
1586
1582
|
|
|
1587
1583
|
// We take into account, that FMS is the same as FMA with a negative multiplier.
|
|
1588
1584
|
// To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
|
|
1589
1585
|
// This way we can avoid the shuffling and the need for separate real and imaginary parts.
|
|
1590
1586
|
// For the imaginary part of the product, we would need to swap the real and imaginary parts of
|
|
1591
1587
|
// one of the vectors.
|
|
1592
|
-
__m512i sign_flip_vec = _mm512_set1_epi32(0x80000000);
|
|
1593
|
-
__m512i swap_adjacent_vec = _mm512_set_epi8(
|
|
1588
|
+
__m512i const sign_flip_vec = _mm512_set1_epi32(0x80000000);
|
|
1589
|
+
__m512i const swap_adjacent_vec = _mm512_set_epi8( //
|
|
1594
1590
|
61, 60, 63, 62, 57, 56, 59, 58, 53, 52, 55, 54, 49, 48, 51, 50, // 4th 128-bit lane
|
|
1595
1591
|
45, 44, 47, 46, 41, 40, 43, 42, 37, 36, 39, 38, 33, 32, 35, 34, // 3rd 128-bit lane
|
|
1596
1592
|
29, 28, 31, 30, 25, 24, 27, 26, 21, 20, 23, 22, 17, 16, 19, 18, // 2nd 128-bit lane
|
|
@@ -1598,22 +1594,22 @@ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_genoa(simsimd_bf16_t const *a, simsimd_bf
|
|
|
1598
1594
|
);
|
|
1599
1595
|
|
|
1600
1596
|
simsimd_dot_bf16c_genoa_cycle:
|
|
1601
|
-
if (
|
|
1602
|
-
__mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF,
|
|
1603
|
-
a_vec = _mm512_maskz_loadu_epi16(mask,
|
|
1604
|
-
b_vec = _mm512_maskz_loadu_epi16(mask,
|
|
1605
|
-
|
|
1597
|
+
if (count_pairs < 16) {
|
|
1598
|
+
__mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
|
|
1599
|
+
a_vec = _mm512_maskz_loadu_epi16(mask, (simsimd_i16_t const *)a_pairs);
|
|
1600
|
+
b_vec = _mm512_maskz_loadu_epi16(mask, (simsimd_i16_t const *)b_pairs);
|
|
1601
|
+
count_pairs = 0;
|
|
1606
1602
|
}
|
|
1607
1603
|
else {
|
|
1608
|
-
a_vec = _mm512_loadu_epi16(
|
|
1609
|
-
b_vec = _mm512_loadu_epi16(
|
|
1610
|
-
|
|
1604
|
+
a_vec = _mm512_loadu_epi16((simsimd_i16_t const *)a_pairs);
|
|
1605
|
+
b_vec = _mm512_loadu_epi16((simsimd_i16_t const *)b_pairs);
|
|
1606
|
+
a_pairs += 16, b_pairs += 16, count_pairs -= 16;
|
|
1611
1607
|
}
|
|
1612
1608
|
ab_real_vec = _mm512_dpbf16_ps(ab_real_vec, (__m512bh)(a_vec), (__m512bh)(b_vec));
|
|
1613
1609
|
a_vec = _mm512_xor_si512(a_vec, sign_flip_vec);
|
|
1614
1610
|
b_vec = _mm512_shuffle_epi8(b_vec, swap_adjacent_vec);
|
|
1615
1611
|
ab_imag_vec = _mm512_dpbf16_ps(ab_imag_vec, (__m512bh)(a_vec), (__m512bh)(b_vec));
|
|
1616
|
-
if (
|
|
1612
|
+
if (count_pairs) goto simsimd_dot_bf16c_genoa_cycle;
|
|
1617
1613
|
|
|
1618
1614
|
// Reduce horizontal sums:
|
|
1619
1615
|
results[0] = _simsimd_reduce_f32x16_skylake(ab_real_vec);
|
|
@@ -1630,43 +1626,42 @@ simsimd_dot_bf16c_genoa_cycle:
|
|
|
1630
1626
|
#pragma clang attribute push(__attribute__((target("avx2,avx512f,avx512vl,bmi2,avx512bw,avx512fp16"))), \
|
|
1631
1627
|
apply_to = function)
|
|
1632
1628
|
|
|
1633
|
-
SIMSIMD_PUBLIC void simsimd_dot_f16_sapphire(simsimd_f16_t const *
|
|
1634
|
-
simsimd_distance_t *result) {
|
|
1635
|
-
__m512h ab_vec = _mm512_setzero_ph();
|
|
1629
|
+
SIMSIMD_PUBLIC void simsimd_dot_f16_sapphire(simsimd_f16_t const *a_scalars, simsimd_f16_t const *b_scalars,
|
|
1630
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
1636
1631
|
__m512i a_i16_vec, b_i16_vec;
|
|
1632
|
+
__m512h ab_vec = _mm512_setzero_ph();
|
|
1637
1633
|
|
|
1638
1634
|
simsimd_dot_f16_sapphire_cycle:
|
|
1639
|
-
if (
|
|
1640
|
-
__mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF,
|
|
1641
|
-
a_i16_vec = _mm512_maskz_loadu_epi16(mask,
|
|
1642
|
-
b_i16_vec = _mm512_maskz_loadu_epi16(mask,
|
|
1643
|
-
|
|
1635
|
+
if (count_scalars < 32) {
|
|
1636
|
+
__mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, count_scalars);
|
|
1637
|
+
a_i16_vec = _mm512_maskz_loadu_epi16(mask, a_scalars);
|
|
1638
|
+
b_i16_vec = _mm512_maskz_loadu_epi16(mask, b_scalars);
|
|
1639
|
+
count_scalars = 0;
|
|
1644
1640
|
}
|
|
1645
1641
|
else {
|
|
1646
|
-
a_i16_vec = _mm512_loadu_epi16(
|
|
1647
|
-
b_i16_vec = _mm512_loadu_epi16(
|
|
1648
|
-
|
|
1642
|
+
a_i16_vec = _mm512_loadu_epi16(a_scalars);
|
|
1643
|
+
b_i16_vec = _mm512_loadu_epi16(b_scalars);
|
|
1644
|
+
a_scalars += 32, b_scalars += 32, count_scalars -= 32;
|
|
1649
1645
|
}
|
|
1650
1646
|
ab_vec = _mm512_fmadd_ph(_mm512_castsi512_ph(a_i16_vec), _mm512_castsi512_ph(b_i16_vec), ab_vec);
|
|
1651
|
-
if (
|
|
1647
|
+
if (count_scalars) goto simsimd_dot_f16_sapphire_cycle;
|
|
1652
1648
|
|
|
1653
1649
|
*result = _mm512_reduce_add_ph(ab_vec);
|
|
1654
1650
|
}
|
|
1655
1651
|
|
|
1656
|
-
SIMSIMD_PUBLIC void simsimd_dot_f16c_sapphire(
|
|
1657
|
-
simsimd_distance_t *results) {
|
|
1652
|
+
SIMSIMD_PUBLIC void simsimd_dot_f16c_sapphire(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
|
|
1653
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
1654
|
+
__m512i a_vec, b_vec;
|
|
1658
1655
|
__m512h ab_real_vec = _mm512_setzero_ph();
|
|
1659
1656
|
__m512h ab_imag_vec = _mm512_setzero_ph();
|
|
1660
|
-
__m512i a_vec;
|
|
1661
|
-
__m512i b_vec;
|
|
1662
1657
|
|
|
1663
1658
|
// We take into account, that FMS is the same as FMA with a negative multiplier.
|
|
1664
1659
|
// To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
|
|
1665
1660
|
// This way we can avoid the shuffling and the need for separate real and imaginary parts.
|
|
1666
1661
|
// For the imaginary part of the product, we would need to swap the real and imaginary parts of
|
|
1667
1662
|
// one of the vectors.
|
|
1668
|
-
__m512i sign_flip_vec = _mm512_set1_epi32(0x80000000);
|
|
1669
|
-
__m512i swap_adjacent_vec = _mm512_set_epi8(
|
|
1663
|
+
__m512i const sign_flip_vec = _mm512_set1_epi32(0x80000000);
|
|
1664
|
+
__m512i const swap_adjacent_vec = _mm512_set_epi8( //
|
|
1670
1665
|
61, 60, 63, 62, 57, 56, 59, 58, 53, 52, 55, 54, 49, 48, 51, 50, // 4th 128-bit lane
|
|
1671
1666
|
45, 44, 47, 46, 41, 40, 43, 42, 37, 36, 39, 38, 33, 32, 35, 34, // 3rd 128-bit lane
|
|
1672
1667
|
29, 28, 31, 30, 25, 24, 27, 26, 21, 20, 23, 22, 17, 16, 19, 18, // 2nd 128-bit lane
|
|
@@ -1674,23 +1669,23 @@ SIMSIMD_PUBLIC void simsimd_dot_f16c_sapphire(simsimd_f16_t const *a, simsimd_f1
|
|
|
1674
1669
|
);
|
|
1675
1670
|
|
|
1676
1671
|
simsimd_dot_f16c_sapphire_cycle:
|
|
1677
|
-
if (
|
|
1678
|
-
__mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF,
|
|
1679
|
-
a_vec = _mm512_maskz_loadu_epi16(mask,
|
|
1680
|
-
b_vec = _mm512_maskz_loadu_epi16(mask,
|
|
1681
|
-
|
|
1672
|
+
if (count_pairs < 16) {
|
|
1673
|
+
__mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
|
|
1674
|
+
a_vec = _mm512_maskz_loadu_epi16(mask, a_pairs);
|
|
1675
|
+
b_vec = _mm512_maskz_loadu_epi16(mask, b_pairs);
|
|
1676
|
+
count_pairs = 0;
|
|
1682
1677
|
}
|
|
1683
1678
|
else {
|
|
1684
|
-
a_vec = _mm512_loadu_epi16(
|
|
1685
|
-
b_vec = _mm512_loadu_epi16(
|
|
1686
|
-
|
|
1679
|
+
a_vec = _mm512_loadu_epi16(a_pairs);
|
|
1680
|
+
b_vec = _mm512_loadu_epi16(b_pairs);
|
|
1681
|
+
a_pairs += 16, b_pairs += 16, count_pairs -= 16;
|
|
1687
1682
|
}
|
|
1688
|
-
// TODO: Consider using `_mm512_fmaddsub`
|
|
1683
|
+
// TODO: Consider using `_mm512_fmaddsub` and `_mm512_fcmadd_pch`
|
|
1689
1684
|
ab_real_vec = _mm512_fmadd_ph(_mm512_castsi512_ph(_mm512_xor_si512(b_vec, sign_flip_vec)),
|
|
1690
1685
|
_mm512_castsi512_ph(a_vec), ab_real_vec);
|
|
1691
1686
|
ab_imag_vec = _mm512_fmadd_ph(_mm512_castsi512_ph(_mm512_shuffle_epi8(b_vec, swap_adjacent_vec)),
|
|
1692
1687
|
_mm512_castsi512_ph(a_vec), ab_imag_vec);
|
|
1693
|
-
if (
|
|
1688
|
+
if (count_pairs) goto simsimd_dot_f16c_sapphire_cycle;
|
|
1694
1689
|
|
|
1695
1690
|
// Reduce horizontal sums:
|
|
1696
1691
|
// TODO: Optimize this with tree-like reductions
|
|
@@ -1698,20 +1693,19 @@ simsimd_dot_f16c_sapphire_cycle:
|
|
|
1698
1693
|
results[1] = _mm512_reduce_add_ph(ab_imag_vec);
|
|
1699
1694
|
}
|
|
1700
1695
|
|
|
1701
|
-
SIMSIMD_PUBLIC void simsimd_vdot_f16c_sapphire(
|
|
1702
|
-
simsimd_distance_t *results) {
|
|
1696
|
+
SIMSIMD_PUBLIC void simsimd_vdot_f16c_sapphire(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
|
|
1697
|
+
simsimd_size_t count_pairs, simsimd_distance_t *results) {
|
|
1698
|
+
__m512i a_vec, b_vec;
|
|
1703
1699
|
__m512h ab_real_vec = _mm512_setzero_ph();
|
|
1704
1700
|
__m512h ab_imag_vec = _mm512_setzero_ph();
|
|
1705
|
-
__m512i a_vec;
|
|
1706
|
-
__m512i b_vec;
|
|
1707
1701
|
|
|
1708
1702
|
// We take into account, that FMS is the same as FMA with a negative multiplier.
|
|
1709
1703
|
// To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
|
|
1710
1704
|
// This way we can avoid the shuffling and the need for separate real and imaginary parts.
|
|
1711
1705
|
// For the imaginary part of the product, we would need to swap the real and imaginary parts of
|
|
1712
1706
|
// one of the vectors.
|
|
1713
|
-
__m512i sign_flip_vec = _mm512_set1_epi32(0x80000000);
|
|
1714
|
-
__m512i swap_adjacent_vec = _mm512_set_epi8(
|
|
1707
|
+
__m512i const sign_flip_vec = _mm512_set1_epi32(0x80000000);
|
|
1708
|
+
__m512i const swap_adjacent_vec = _mm512_set_epi8( //
|
|
1715
1709
|
61, 60, 63, 62, 57, 56, 59, 58, 53, 52, 55, 54, 49, 48, 51, 50, // 4th 128-bit lane
|
|
1716
1710
|
45, 44, 47, 46, 41, 40, 43, 42, 37, 36, 39, 38, 33, 32, 35, 34, // 3rd 128-bit lane
|
|
1717
1711
|
29, 28, 31, 30, 25, 24, 27, 26, 21, 20, 23, 22, 17, 16, 19, 18, // 2nd 128-bit lane
|
|
@@ -1719,22 +1713,23 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c_sapphire(simsimd_f16_t const *a, simsimd_f
|
|
|
1719
1713
|
);
|
|
1720
1714
|
|
|
1721
1715
|
simsimd_dot_f16c_sapphire_cycle:
|
|
1722
|
-
if (
|
|
1723
|
-
__mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF,
|
|
1724
|
-
a_vec = _mm512_maskz_loadu_epi16(mask,
|
|
1725
|
-
b_vec = _mm512_maskz_loadu_epi16(mask,
|
|
1726
|
-
|
|
1716
|
+
if (count_pairs < 16) {
|
|
1717
|
+
__mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
|
|
1718
|
+
a_vec = _mm512_maskz_loadu_epi16(mask, a_pairs);
|
|
1719
|
+
b_vec = _mm512_maskz_loadu_epi16(mask, b_pairs);
|
|
1720
|
+
count_pairs = 0;
|
|
1727
1721
|
}
|
|
1728
1722
|
else {
|
|
1729
|
-
a_vec = _mm512_loadu_epi16(
|
|
1730
|
-
b_vec = _mm512_loadu_epi16(
|
|
1731
|
-
|
|
1723
|
+
a_vec = _mm512_loadu_epi16(a_pairs);
|
|
1724
|
+
b_vec = _mm512_loadu_epi16(b_pairs);
|
|
1725
|
+
a_pairs += 16, b_pairs += 16, count_pairs -= 16;
|
|
1732
1726
|
}
|
|
1727
|
+
// TODO: Consider using `_mm512_fmaddsub` and `_mm512_fcmadd_pch`
|
|
1733
1728
|
ab_real_vec = _mm512_fmadd_ph(_mm512_castsi512_ph(a_vec), _mm512_castsi512_ph(b_vec), ab_real_vec);
|
|
1734
1729
|
a_vec = _mm512_xor_si512(a_vec, sign_flip_vec);
|
|
1735
1730
|
b_vec = _mm512_shuffle_epi8(b_vec, swap_adjacent_vec);
|
|
1736
1731
|
ab_imag_vec = _mm512_fmadd_ph(_mm512_castsi512_ph(a_vec), _mm512_castsi512_ph(b_vec), ab_imag_vec);
|
|
1737
|
-
if (
|
|
1732
|
+
if (count_pairs) goto simsimd_dot_f16c_sapphire_cycle;
|
|
1738
1733
|
|
|
1739
1734
|
// Reduce horizontal sums:
|
|
1740
1735
|
results[0] = _mm512_reduce_add_ph(ab_real_vec);
|
|
@@ -1751,53 +1746,53 @@ simsimd_dot_f16c_sapphire_cycle:
|
|
|
1751
1746
|
#pragma clang attribute push(__attribute__((target("avx2,avx512f,avx512vl,bmi2,avx512bw,avx512vnni"))), \
|
|
1752
1747
|
apply_to = function)
|
|
1753
1748
|
|
|
1754
|
-
SIMSIMD_PUBLIC void simsimd_dot_i8_ice(simsimd_i8_t const *
|
|
1755
|
-
simsimd_distance_t *result) {
|
|
1756
|
-
__m512i ab_i32_vec = _mm512_setzero_si512();
|
|
1749
|
+
SIMSIMD_PUBLIC void simsimd_dot_i8_ice(simsimd_i8_t const *a_scalars, simsimd_i8_t const *b_scalars,
|
|
1750
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
1757
1751
|
__m512i a_i16_vec, b_i16_vec;
|
|
1752
|
+
__m512i ab_i32_vec = _mm512_setzero_si512();
|
|
1758
1753
|
|
|
1759
1754
|
simsimd_dot_i8_ice_cycle:
|
|
1760
|
-
if (
|
|
1761
|
-
__mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF,
|
|
1762
|
-
a_i16_vec = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask,
|
|
1763
|
-
b_i16_vec = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask,
|
|
1764
|
-
|
|
1755
|
+
if (count_scalars < 32) {
|
|
1756
|
+
__mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, count_scalars);
|
|
1757
|
+
a_i16_vec = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask, a_scalars));
|
|
1758
|
+
b_i16_vec = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask, b_scalars));
|
|
1759
|
+
count_scalars = 0;
|
|
1765
1760
|
}
|
|
1766
1761
|
else {
|
|
1767
|
-
a_i16_vec = _mm512_cvtepi8_epi16(_mm256_lddqu_si256((__m256i const *)
|
|
1768
|
-
b_i16_vec = _mm512_cvtepi8_epi16(_mm256_lddqu_si256((__m256i const *)
|
|
1769
|
-
|
|
1762
|
+
a_i16_vec = _mm512_cvtepi8_epi16(_mm256_lddqu_si256((__m256i const *)a_scalars));
|
|
1763
|
+
b_i16_vec = _mm512_cvtepi8_epi16(_mm256_lddqu_si256((__m256i const *)b_scalars));
|
|
1764
|
+
a_scalars += 32, b_scalars += 32, count_scalars -= 32;
|
|
1770
1765
|
}
|
|
1771
1766
|
// Unfortunately we can't use the `_mm512_dpbusd_epi32` intrinsics here either,
|
|
1772
1767
|
// as it's asymmetric with respect to the sign of the input arguments:
|
|
1773
|
-
// Signed(ZeroExtend16(
|
|
1768
|
+
// Signed(ZeroExtend16(a_scalars.byte[4*j]) * SignExtend16(b_scalars.byte[4*j]))
|
|
1774
1769
|
// So we have to use the `_mm512_dpwssd_epi32` intrinsics instead, upcasting
|
|
1775
1770
|
// to 16-bit beforehand.
|
|
1776
1771
|
ab_i32_vec = _mm512_dpwssd_epi32(ab_i32_vec, a_i16_vec, b_i16_vec);
|
|
1777
|
-
if (
|
|
1772
|
+
if (count_scalars) goto simsimd_dot_i8_ice_cycle;
|
|
1778
1773
|
|
|
1779
1774
|
*result = _mm512_reduce_add_epi32(ab_i32_vec);
|
|
1780
1775
|
}
|
|
1781
1776
|
|
|
1782
|
-
SIMSIMD_PUBLIC void simsimd_dot_u8_ice(simsimd_u8_t const *
|
|
1783
|
-
simsimd_distance_t *result) {
|
|
1777
|
+
SIMSIMD_PUBLIC void simsimd_dot_u8_ice(simsimd_u8_t const *a_scalars, simsimd_u8_t const *b_scalars,
|
|
1778
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
1779
|
+
__m512i a_u8_vec, b_u8_vec;
|
|
1780
|
+
__m512i a_i16_low_vec, a_i16_high_vec, b_i16_low_vec, b_i16_high_vec;
|
|
1784
1781
|
__m512i ab_i32_low_vec = _mm512_setzero_si512();
|
|
1785
1782
|
__m512i ab_i32_high_vec = _mm512_setzero_si512();
|
|
1786
1783
|
__m512i const zeros_vec = _mm512_setzero_si512();
|
|
1787
|
-
__m512i a_i16_low_vec, a_i16_high_vec, b_i16_low_vec, b_i16_high_vec;
|
|
1788
|
-
__m512i a_u8_vec, b_u8_vec;
|
|
1789
1784
|
|
|
1790
1785
|
simsimd_dot_u8_ice_cycle:
|
|
1791
|
-
if (
|
|
1792
|
-
__mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF,
|
|
1793
|
-
a_u8_vec = _mm512_maskz_loadu_epi8(mask,
|
|
1794
|
-
b_u8_vec = _mm512_maskz_loadu_epi8(mask,
|
|
1795
|
-
|
|
1786
|
+
if (count_scalars < 64) {
|
|
1787
|
+
__mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, count_scalars);
|
|
1788
|
+
a_u8_vec = _mm512_maskz_loadu_epi8(mask, a_scalars);
|
|
1789
|
+
b_u8_vec = _mm512_maskz_loadu_epi8(mask, b_scalars);
|
|
1790
|
+
count_scalars = 0;
|
|
1796
1791
|
}
|
|
1797
1792
|
else {
|
|
1798
|
-
a_u8_vec = _mm512_loadu_si512(
|
|
1799
|
-
b_u8_vec = _mm512_loadu_si512(
|
|
1800
|
-
|
|
1793
|
+
a_u8_vec = _mm512_loadu_si512(a_scalars);
|
|
1794
|
+
b_u8_vec = _mm512_loadu_si512(b_scalars);
|
|
1795
|
+
a_scalars += 64, b_scalars += 64, count_scalars -= 64;
|
|
1801
1796
|
}
|
|
1802
1797
|
|
|
1803
1798
|
// Upcast `uint8` to `int16`. Unlike the signed version, we can use the unpacking
|
|
@@ -1813,7 +1808,7 @@ simsimd_dot_u8_ice_cycle:
|
|
|
1813
1808
|
// to 16-bit beforehand.
|
|
1814
1809
|
ab_i32_low_vec = _mm512_dpwssd_epi32(ab_i32_low_vec, a_i16_low_vec, b_i16_low_vec);
|
|
1815
1810
|
ab_i32_high_vec = _mm512_dpwssd_epi32(ab_i32_high_vec, a_i16_high_vec, b_i16_high_vec);
|
|
1816
|
-
if (
|
|
1811
|
+
if (count_scalars) goto simsimd_dot_u8_ice_cycle;
|
|
1817
1812
|
|
|
1818
1813
|
*result = _mm512_reduce_add_epi32(_mm512_add_epi32(ab_i32_low_vec, ab_i32_high_vec));
|
|
1819
1814
|
}
|
|
@@ -1827,15 +1822,14 @@ simsimd_dot_u8_ice_cycle:
|
|
|
1827
1822
|
#pragma GCC target("avx2", "bmi2", "avx2vnni")
|
|
1828
1823
|
#pragma clang attribute push(__attribute__((target("avx2,bmi2,avx2vnni"))), apply_to = function)
|
|
1829
1824
|
|
|
1830
|
-
SIMSIMD_PUBLIC void simsimd_dot_i8_sierra(simsimd_i8_t const *
|
|
1831
|
-
simsimd_distance_t *result) {
|
|
1825
|
+
SIMSIMD_PUBLIC void simsimd_dot_i8_sierra(simsimd_i8_t const *a_scalars, simsimd_i8_t const *b_scalars,
|
|
1826
|
+
simsimd_size_t count_scalars, simsimd_distance_t *result) {
|
|
1832
1827
|
|
|
1833
1828
|
__m256i ab_i32_vec = _mm256_setzero_si256();
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
__m256i
|
|
1838
|
-
__m256i b_i8_vec = _mm256_lddqu_si256((__m256i const *)(b + i));
|
|
1829
|
+
simsimd_size_t idx_scalars = 0;
|
|
1830
|
+
for (; idx_scalars + 32 <= count_scalars; idx_scalars += 32) {
|
|
1831
|
+
__m256i a_i8_vec = _mm256_lddqu_si256((__m256i const *)(a_scalars + idx_scalars));
|
|
1832
|
+
__m256i b_i8_vec = _mm256_lddqu_si256((__m256i const *)(b_scalars + idx_scalars));
|
|
1839
1833
|
ab_i32_vec = _mm256_dpbssds_epi32(ab_i32_vec, a_i8_vec, b_i8_vec);
|
|
1840
1834
|
}
|
|
1841
1835
|
|
|
@@ -1843,8 +1837,7 @@ SIMSIMD_PUBLIC void simsimd_dot_i8_sierra(simsimd_i8_t const *a, simsimd_i8_t co
|
|
|
1843
1837
|
int ab = _simsimd_reduce_i32x8_haswell(ab_i32_vec);
|
|
1844
1838
|
|
|
1845
1839
|
// Take care of the tail:
|
|
1846
|
-
for (;
|
|
1847
|
-
|
|
1840
|
+
for (; idx_scalars < count_scalars; ++idx_scalars) ab += (int)(a_scalars[idx_scalars]) * b_scalars[idx_scalars];
|
|
1848
1841
|
*result = ab;
|
|
1849
1842
|
}
|
|
1850
1843
|
|