usearch 2.1.1 → 2.1.3

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.
@@ -34,14 +34,17 @@ endif()
34
34
 
35
35
  set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -fsanitize=address")
36
36
  set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
37
+ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -fsanitize=address")
38
+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
37
39
 
38
40
  # List of all possible compiler IDs:
39
41
  # https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html
40
42
  if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
41
43
  elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
42
44
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
43
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
44
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmax-errors=1")
45
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -fmax-errors=1")
46
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native")
47
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic -fmax-errors=1")
45
48
  elseif(CMAKE_CXX_COMPILER_ID STREQUAL "NVIDIA" OR CMAKE_CXX_COMPILER_ID STREQUAL "NVHPC")
46
49
  set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} --expt-relaxed-constexpr --extended-lambda")
47
50
  set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=compute_86,code=sm_86")
package/simsimd/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # SimSIMD
1
+ # SimSIMD 📏
2
2
 
3
- SIMD-accelerated similarity measures, metrics, distance functions for x86 and Arm.
4
- Tuned for Machine Learning applications, mid-size vectors with 100-1024 dimensions.
5
- For Cosine (Angular) distance, the most common metric in AI, one can expect following performance.
3
+ SIMD-accelerated similarity measures, metrics, and distance functions for x86 and Arm.
4
+ They are tuned for Machine Learning applications and mid-size vectors with 100-1024 dimensions.
5
+ One can expect the following performance for Cosine (Angular) distance, the most common metric in AI.
6
6
 
7
7
  | Method | Vectors | Any Length | Speed on 256b | Speed on 1024b |
8
8
  | :----- | :------ | :--------- | ------------: | -------------: |
@@ -12,7 +12,7 @@ For Cosine (Angular) distance, the most common metric in AI, one can expect foll
12
12
  | NEON | `f16` | ❌ | 16 GB/s | 18 GB/s |
13
13
 
14
14
  > The benchmarks were done on Arm-based "Graviton 3" CPUs powering AWS `c7g.metal` instances.
15
- > We only use Arm NEON implementation with vectors lengths that are multiples of 128 bits, avoiding any additional head or tail `for` loops for misaligned data.
15
+ > We only use Arm NEON implementation with vector lengths multiples of 128 bits, avoiding additional head or tail `for` loops for misaligned data.
16
16
  > By default, we use GCC12, `-O3`, `-march=native` for benchmarks.
17
17
  > Serial versions imply auto-vectorization pragmas.
18
18
 
@@ -28,7 +28,7 @@ FetchContent_MakeAvailable(simsimd)
28
28
  include_directories(${simsimd_SOURCE_DIR}/include)
29
29
  ```
30
30
 
31
- Want to use in Python with [USearch](https://github.com/unum-cloud/usearch)?
31
+ Want to use it in Python with [USearch](https://github.com/unum-cloud/usearch)?
32
32
 
33
33
  ```py
34
34
  from usearch import Index, CompiledMetric, MetricKind, MetricSignature
@@ -45,60 +45,81 @@ index = Index(256, metric=metric)
45
45
 
46
46
  ## Available Metrics
47
47
 
48
- In the C99 interface all function are prepended with `simsimd_` namespace prefix.
49
- Signature defines the number of arguments:
48
+ In the C99 interface, all functions are prepended with the `simsimd_` namespace prefix.
49
+ The signature defines the number of arguments:
50
50
 
51
51
  - two pointers, and length,
52
52
  - two pointers.
53
53
 
54
- The latter is obviously intended for cases, where the number of dimensions is hard-coded.
55
- Constraints define the limitations on the number of dimensions that an argument vector can have.
56
-
57
- | Name | Signature | ISA Extesion | Constraints |
58
- | :---------------------- | :-------: | :----------: | :------------: |
59
- | `dot_f32_sve` | ✳️✳️📏 | Arm SVE | |
60
- | `dot_f32x4_neon` | ✳️✳️📏 | Arm NEON | `d % 4 == 0` |
61
- | `cos_f32_sve` | ✳️✳️📏 | Arm SVE | |
62
- | `cos_f16_sve` | ✳️✳️📏 | Arm SVE | |
63
- | `cos_f16x4_neon` | ✳️✳️📏 | Arm NEON | `d % 4 == 0` |
64
- | `cos_i8x16_neon` | ✳️✳️📏 | Arm NEON | `d % 16 == 0` |
65
- | `cos_f32x4_neon` | ✳️✳️📏 | Arm NEON | `d % 4 == 0` |
66
- | `cos_f16x16_avx512` | ✳️✳️📏 | x86 AVX-512 | `d % 16 == 0` |
67
- | `cos_f32x4_avx2` | ✳️✳️📏 | x86 AVX2 | `d % 4 == 0` |
68
- | `l2sq_f32_sve` | ✳️✳️📏 | Arm SVE | |
69
- | `l2sq_f16_sve` | ✳️✳️📏 | Arm SVE | |
70
- | `hamming_b1x8_sve` | ✳️✳️📏 | Arm SVE | `d % 8 == 0` |
71
- | `hamming_b1x128_sve` | ✳️✳️📏 | Arm SVE | `d % 128 == 0` |
72
- | `hamming_b1x128_avx512` | ✳️✳️📏 | x86 AVX-512 | `d % 128 == 0` |
73
- | `tanimoto_b1x8_naive` | ✳️✳️📏 | | `d % 8 == 0` |
74
- | `tanimoto_maccs_naive` | ✳️✳️ | | `d == 166` |
75
- | `tanimoto_maccs_neon` | ✳️✳️ | Arm NEON | `d == 166` |
76
- | `tanimoto_maccs_sve` | ✳️✳️ | Arm SVE | `d == 166` |
77
- | `tanimoto_maccs_avx512` | ✳️✳️ | x86 AVX-512 | `d == 166` |
54
+ The latter is intended for cases where the number of dimensions is hard-coded.
55
+ Constraints define the limitations on the number of dimensions an argument vector can have.
56
+
57
+ | Name | Signature | ISA Extension | Constraints |
58
+ | :---------------------- | :-------: | :-----------: | :------------: |
59
+ | `dot_f32_sve` | ✳️✳️📏 | Arm SVE | |
60
+ | `dot_f32x4_neon` | ✳️✳️📏 | Arm NEON | `d % 4 == 0` |
61
+ | `cos_f32_sve` | ✳️✳️📏 | Arm SVE | |
62
+ | `cos_f16_sve` | ✳️✳️📏 | Arm SVE | |
63
+ | `cos_f16x4_neon` | ✳️✳️📏 | Arm NEON | `d % 4 == 0` |
64
+ | `cos_i8x16_neon` | ✳️✳️📏 | Arm NEON | `d % 16 == 0` |
65
+ | `cos_f32x4_neon` | ✳️✳️📏 | Arm NEON | `d % 4 == 0` |
66
+ | `cos_f16x16_avx512` | ✳️✳️📏 | x86 AVX-512 | `d % 16 == 0` |
67
+ | `cos_f32x4_avx2` | ✳️✳️📏 | x86 AVX2 | `d % 4 == 0` |
68
+ | `l2sq_f32_sve` | ✳️✳️📏 | Arm SVE | |
69
+ | `l2sq_f16_sve` | ✳️✳️📏 | Arm SVE | |
70
+ | `hamming_b1x8_sve` | ✳️✳️📏 | Arm SVE | `d % 8 == 0` |
71
+ | `hamming_b1x128_sve` | ✳️✳️📏 | Arm SVE | `d % 128 == 0` |
72
+ | `hamming_b1x128_avx512` | ✳️✳️📏 | x86 AVX-512 | `d % 128 == 0` |
73
+ | `tanimoto_b1x8_naive` | ✳️✳️📏 | | `d % 8 == 0` |
74
+ | `tanimoto_maccs_naive` | ✳️✳️ | | `d == 166` |
75
+ | `tanimoto_maccs_neon` | ✳️✳️ | Arm NEON | `d == 166` |
76
+ | `tanimoto_maccs_sve` | ✳️✳️ | Arm SVE | `d == 166` |
77
+ | `tanimoto_maccs_avx512` | ✳️✳️ | x86 AVX-512 | `d == 166` |
78
78
 
79
79
  ## Benchmarks
80
80
 
81
81
  The benchmarks are repeated for every function with a different number of cores involved.
82
- Light-weight distance functions would be memory bound, implying that multi-core performance may be lower, if the bus bandwidth is not enough to saturate all the cores.
82
+ Light-weight distance functions would be memory bound, implying that multi-core performance may be lower if the bus bandwidth cannot saturate all the cores.
83
83
  Similarly, heavy-weight distance functions running on all cores may result in CPU frequency downclocking.
84
- This is well illustrated by the single-core performance of `i9-13950HX`, equipped with DDR5 memory.
85
-
86
- | Method | Threads | Speed on 1024b |
87
- | :--------------------- | ------: | -------------: |
88
- | `tanimoto_maccs_naive` | 1 | 2.8 Gb/s |
89
- | `tanimoto_maccs_naive` | 32 | 1.2 Gb/s |
90
- | `cos_f32_naive` | 1 | 15.3 Gb/s |
91
- | `cos_f32_naive` | 32 | 4.5 Gb/s |
92
- | `dot_f32x4_avx2` | 1 | 96.2 Gb/s |
93
- | `dot_f32x4_avx2` | 32 | 23.6 Gb/s |
94
- | `cos_f32x4_avx2` | 1 | 56.3 Gb/s |
95
- | `cos_f32x4_avx2` | 32 | 13.9 Gb/s |
96
-
97
- To replicate on your hardware, please run following on Linux:
84
+ This is well illustrated by the single-core performance of the Intel `i9-13950HX`, equipped with DDR5 memory.
85
+
86
+ | Method | Threads | Vector Size | Speed |
87
+ | :--------------------- | ------: | ----------: | --------: |
88
+ | `dot_f32x4_avx2` | 1 | 1024 b | 96.2 GB/s |
89
+ | `dot_f32x4_avx2` | 32 | 1024 b | 23.6 GB/s |
90
+ | `cos_f32_naive` | 1 | 1024 b | 15.3 GB/s |
91
+ | `cos_f32_naive` | 32 | 1024 b | 4.5 GB/s |
92
+ | `cos_f32x4_avx2` | 1 | 1024 b | 56.3 GB/s |
93
+ | `cos_f32x4_avx2` | 32 | 1024 b | 13.9 GB/s |
94
+ | `tanimoto_maccs_naive` | 1 | 21 b | 2.8 GB/s |
95
+ | `tanimoto_maccs_naive` | 32 | 21 b | 1.2 GB/s |
96
+
97
+ Switching to the Intel Sapphire Rapids server platform, we can also evaluate some of the AVX-512 extensions, including `VPOPCNTDQ` and `F16`.
98
+
99
+ | Method | Threads | Vector Size | Speed |
100
+ | :---------------------- | ------: | ----------: | ---------: |
101
+ | `dot_f32x4_avx2` | 1 | 1024 b | 57.8 GB/s |
102
+ | `dot_f32x4_avx2` | 224 | 1024 b | 16.1 GB/s |
103
+ | `cos_f32_naive` | 1 | 1024 b | 10.7 GB/s |
104
+ | `cos_f32_naive` | 224 | 1024 b | 3.0 GB/s |
105
+ | `cos_f32x4_avx2` | 1 | 1024 b | 39.5 GB/s |
106
+ | `cos_f32x4_avx2` | 224 | 1024 b | 15.1 GB/s |
107
+ | `cos_f16x16_avx512` | 1 | 1024 b | 50.6 GB/s |
108
+ | `cos_f16x16_avx512` | 224 | 1024 b | 15.9 GB/s |
109
+ | `hamming_b1x128_avx512` | 1 | 1024 b | 790.3 GB/s |
110
+ | `hamming_b1x128_avx512` | 224 | 1024 b | 259.3 GB/s |
111
+ | `tanimoto_maccs_naive` | 1 | 21 b | 3.0 GB/s |
112
+ | `tanimoto_maccs_naive` | 224 | 21 b | 1.3 GB/s |
113
+ | `tanimoto_maccs_avx512` | 1 | 21 b | 13.1 GB/s |
114
+ | `tanimoto_maccs_avx512` | 224 | 21 b | 3.7 GB/s |
115
+
116
+ To replicate this on your hardware, please run the following on Linux:
98
117
 
99
118
  ```sh
100
119
  git clone https://github.com/ashvardanian/SimSIMD.git && cd SimSIMD
101
- cmake -DCMAKE_BUILD_TYPE=Release -DSIMSIMD_BUILD_BENCHMARKS=1 -B ./build && make -C ./build && ./build/simsimd_bench
120
+ cmake -DCMAKE_BUILD_TYPE=Release -DSIMSIMD_BUILD_BENCHMARKS=1 \
121
+ -DCMAKE_CXX_COMPILER="g++-12" -DCMAKE_C_COMPILER="gcc-12" \
122
+ -B ./build && make -C ./build && ./build/simsimd_bench
102
123
  ```
103
124
 
104
125
  MacOS:
package/simsimd/bench.cxx CHANGED
@@ -100,7 +100,7 @@ int main(int argc, char** argv) {
100
100
  register_<std::uint8_t, 21>("tanimoto_maccs_naive", simsimd_tanimoto_maccs_naive);
101
101
  register_<simsimd_f32_t>("cos_f32_naive", cos_f32_naive);
102
102
 
103
- #if defined(__ARM_FEATURE_SVE)
103
+ #if SIMSIMD_TARGET_ARM_SVE
104
104
  register_<simsimd_f32_t>("dot_f32_sve", simsimd_dot_f32_sve);
105
105
  register_<simsimd_f32_t>("cos_f32_sve", simsimd_cos_f32_sve);
106
106
  register_<std::int16_t>("cos_f16_sve", simsimd_cos_f16_sve);
@@ -111,27 +111,26 @@ int main(int argc, char** argv) {
111
111
  register_<std::uint8_t, 21>("tanimoto_maccs_sve", simsimd_tanimoto_maccs_sve);
112
112
  #endif
113
113
 
114
- #if defined(__ARM_NEON)
114
+ #if SIMSIMD_TARGET_ARM_NEON
115
115
  register_<simsimd_f32_t>("dot_f32x4_neon", simsimd_dot_f32x4_neon);
116
116
  register_<std::int16_t>("cos_f16x4_neon", simsimd_cos_f16x4_neon);
117
117
  register_<std::int8_t>("cos_i8x16_neon", simsimd_cos_i8x16_neon);
118
+ register_<std::int8_t>("l2sq_i8x16_neon", simsimd_l2sq_i8x16_neon);
118
119
  register_<simsimd_f32_t>("cos_f32x4_neon", simsimd_cos_f32x4_neon);
119
120
  register_<std::uint8_t, 21>("tanimoto_maccs_neon", simsimd_tanimoto_maccs_neon);
120
121
  #endif
121
122
 
122
- #if defined(__AVX2__)
123
+ #if SIMSIMD_TARGET_X86_AVX2
123
124
  register_<simsimd_f32_t>("dot_f32x4_avx2", simsimd_dot_f32x4_avx2);
124
125
  register_<simsimd_f32_t>("cos_f32x4_avx2", simsimd_cos_f32x4_avx2);
125
126
  #endif
126
127
 
127
- #if defined(__AVX512F__)
128
+ #if SIMSIMD_TARGET_X86_AVX512
128
129
  register_<std::int16_t>("cos_f16x16_avx512", simsimd_cos_f16x16_avx512);
129
130
  register_<std::uint8_t>("hamming_b1x128_avx512", simsimd_hamming_b1x128_avx512);
130
131
  register_<std::uint8_t, 21>("tanimoto_maccs_avx512", simsimd_tanimoto_maccs_avx512);
131
132
  #endif
132
133
 
133
-
134
-
135
134
  bm::RunSpecifiedBenchmarks();
136
135
  bm::Shutdown();
137
136
  return 0;