usearch 0.2.2 → 0.2.4

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/src/advanced.hpp CHANGED
@@ -14,6 +14,10 @@
14
14
  #include <thread> // `std::thread`
15
15
  #include <vector> // `std::vector`
16
16
 
17
+ #if defined(_OPENMP)
18
+ #include <omp.h> // `omp_get_num_threads()`
19
+ #endif
20
+
17
21
  #if __linux__
18
22
  #include <sys/auxv.h>
19
23
  #endif
@@ -170,6 +174,29 @@ class f8_bits_t {
170
174
 
171
175
  inline f16_bits_t::f16_bits_t(f8_bits_t v) noexcept : f16_bits_t(float(v)) {}
172
176
 
177
+ struct cos_f8_t {
178
+ punned_distance_t operator()(f8_bits_t const* a, f8_bits_t const* b, std::size_t n) const noexcept {
179
+ std::int32_t ab{}, a2{}, b2{};
180
+ for (std::size_t i = 0; i != n; i++) {
181
+ std::int16_t ai{a[i]};
182
+ std::int16_t bi{b[i]};
183
+ ab += ai * bi;
184
+ a2 += square(ai);
185
+ b2 += square(bi);
186
+ }
187
+ return 1.f - ab / (std::sqrt(a2) * std::sqrt(b2));
188
+ }
189
+ };
190
+
191
+ struct l2sq_f8_t {
192
+ punned_distance_t operator()(f8_bits_t const* a, f8_bits_t const* b, std::size_t n) const noexcept {
193
+ std::int32_t ab_deltas_sq{};
194
+ for (std::size_t i = 0; i != n; i++)
195
+ ab_deltas_sq += square(std::int16_t(a[i]) - std::int16_t(b[i]));
196
+ return ab_deltas_sq;
197
+ }
198
+ };
199
+
173
200
  struct uuid_t {
174
201
  std::uint8_t octets[16];
175
202
  };
@@ -179,12 +206,18 @@ void multithreaded(std::size_t threads, std::size_t tasks, callback_at&& callbac
179
206
 
180
207
  if (threads == 0)
181
208
  threads = std::thread::hardware_concurrency();
182
- if (threads == 1) {
209
+ if (threads == 1 || tasks <= 128) {
183
210
  for (std::size_t task_idx = 0; task_idx < tasks; ++task_idx)
184
211
  callback(0, task_idx);
185
212
  return;
186
213
  }
187
214
 
215
+ #if defined(_OPENMP)
216
+ omp_set_num_threads(threads);
217
+ #pragma omp parallel for schedule(dynamic)
218
+ for (std::size_t i = 0; i < tasks; ++i)
219
+ callback(omp_get_thread_num(), i);
220
+ #else
188
221
  std::vector<std::thread> threads_pool;
189
222
  std::size_t tasks_per_thread = (tasks / threads) + ((tasks % threads) != 0);
190
223
  for (std::size_t thread_idx = 0; thread_idx != threads; ++thread_idx) {
@@ -194,9 +227,9 @@ void multithreaded(std::size_t threads, std::size_t tasks, callback_at&& callbac
194
227
  callback(thread_idx, task_idx);
195
228
  });
196
229
  }
197
-
198
230
  for (std::size_t thread_idx = 0; thread_idx != threads; ++thread_idx)
199
231
  threads_pool[thread_idx].join();
232
+ #endif
200
233
  }
201
234
 
202
235
  /**
@@ -424,6 +457,7 @@ class auto_index_gt {
424
457
  config_t const& config() const noexcept { return root_config_; }
425
458
  void clear() noexcept { return index_->clear(); }
426
459
 
460
+ accuracy_t accuracy() const noexcept { return accuracy_; }
427
461
  isa_t acceleration() const noexcept { return acceleration_; }
428
462
  std::size_t concurrency() const noexcept {
429
463
  return (std::min)(root_config_.max_threads_add, root_config_.max_threads_search);
@@ -484,7 +518,11 @@ class auto_index_gt {
484
518
 
485
519
  private:
486
520
  static index_t* aligned_index_alloc() noexcept {
521
+ #if defined(WINDOWS)
522
+ return (index_t*)_aligned_malloc(64, 64 * divide_round_up<64>(sizeof(index_t)));
523
+ #else
487
524
  return (index_t*)aligned_alloc(64, 64 * divide_round_up<64>(sizeof(index_t)));
525
+ #endif
488
526
  }
489
527
 
490
528
  static void aligned_index_free(index_t* raw) noexcept { free(raw); }
@@ -641,43 +679,40 @@ class auto_index_gt {
641
679
  return {pun_metric<f32_t>(ip_gt<f32_t>{}), isa_t::auto_k};
642
680
  }
643
681
 
644
- static metric_and_meta_t ip_metric_f16(std::size_t dimensions) {
682
+ static metric_and_meta_t cos_metric_f16(std::size_t dimensions) {
645
683
  (void)dimensions;
646
684
  #if defined(USEARCH_USE_SIMD)
647
685
  #if defined(__x86_64__)
648
- if (dimensions % 32 == 0)
686
+ if (dimensions % 16 == 0)
649
687
  return {
650
688
  pun_metric<simsimd_f16_t>([](simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) noexcept {
651
- return 1.f - simsimd_dot_f16x16avx512(a, b, d);
689
+ return 1.f - simsimd_cos_f16x16avx512(a, b, d);
652
690
  }),
653
691
  isa_t::avx512_k,
654
692
  };
655
693
  #elif defined(__aarch64__)
656
- if (supports_arm_sve())
657
- return {
658
- pun_metric<simsimd_f16_t>([](simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) noexcept {
659
- return 1.f - simsimd_dot_f16sve(a, b, d);
660
- }),
661
- isa_t::sve_k,
662
- };
663
- if (dimensions % 8 == 0)
694
+ if (dimensions % 4 == 0)
664
695
  return {
665
696
  pun_metric<simsimd_f16_t>([](simsimd_f16_t const* a, simsimd_f16_t const* b, size_t d) noexcept {
666
- return 1.f - simsimd_dot_f16x8neon(a, b, d);
697
+ return 1.f - simsimd_cos_f16x4neon(a, b, d);
667
698
  }),
668
699
  isa_t::neon_k,
669
700
  };
670
701
  #endif
671
702
  #endif
672
- return {pun_metric<f16_bits_t>(ip_gt<f16_bits_t, f32_t>{}), isa_t::auto_k};
703
+ return {pun_metric<f16_bits_t>(cos_gt<f16_bits_t, f32_t>{}), isa_t::auto_k};
673
704
  }
674
705
 
675
706
  static metric_and_meta_t ip_metric(std::size_t dimensions, accuracy_t accuracy) {
676
707
  switch (accuracy) {
677
- // The tow most common numeric types for the most common metric have afew optimized versions:
678
- case accuracy_t::f16_k: return ip_metric_f16(dimensions);
679
- case accuracy_t::f32_k: return ip_metric_f32(dimensions);
680
- case accuracy_t::f8_k: return {pun_metric<f8_bits_t>(ip_gt<f8_bits_t, f32_t>{}), isa_t::auto_k};
708
+ case accuracy_t::f32_k:
709
+ // The two most common numeric types for the most common metric have optimized versions
710
+ return ip_metric_f32(dimensions);
711
+ case accuracy_t::f16_k:
712
+ // Dot-product accumulates error, Cosine-distance normalizes it
713
+ return cos_metric_f16(dimensions);
714
+
715
+ case accuracy_t::f8_k: return {pun_metric<f8_bits_t>(cos_f8_t{}), isa_t::auto_k};
681
716
  case accuracy_t::f64_k: return {pun_metric<f64_t>(ip_gt<f64_t>{}), isa_t::auto_k};
682
717
  default: return {};
683
718
  }
@@ -685,7 +720,7 @@ class auto_index_gt {
685
720
 
686
721
  static metric_and_meta_t l2_metric(std::size_t, accuracy_t accuracy) {
687
722
  switch (accuracy) {
688
- case accuracy_t::f8_k: return {pun_metric<f8_bits_t>(l2sq_gt<f8_bits_t, f32_t>{}), isa_t::auto_k};
723
+ case accuracy_t::f8_k: return {pun_metric<f8_bits_t>(l2sq_f8_t{}), isa_t::auto_k};
689
724
  case accuracy_t::f16_k: return {pun_metric<f16_bits_t>(l2sq_gt<f16_bits_t, f32_t>{}), isa_t::auto_k};
690
725
  case accuracy_t::f32_k: return {pun_metric<f32_t>(l2sq_gt<f32_t>{}), isa_t::auto_k};
691
726
  case accuracy_t::f64_k: return {pun_metric<f64_t>(l2sq_gt<f64_t>{}), isa_t::auto_k};
@@ -693,10 +728,10 @@ class auto_index_gt {
693
728
  }
694
729
  }
695
730
 
696
- static metric_and_meta_t cos_metric(std::size_t, accuracy_t accuracy) {
731
+ static metric_and_meta_t cos_metric(std::size_t dimensions, accuracy_t accuracy) {
697
732
  switch (accuracy) {
698
- case accuracy_t::f8_k: return {pun_metric<f8_bits_t>(cos_gt<f8_bits_t, f32_t>{}), isa_t::auto_k};
699
- case accuracy_t::f16_k: return {pun_metric<f16_bits_t>(cos_gt<f16_bits_t, f32_t>{}), isa_t::auto_k};
733
+ case accuracy_t::f8_k: return {pun_metric<f8_bits_t>(cos_f8_t{}), isa_t::auto_k};
734
+ case accuracy_t::f16_k: return cos_metric_f16(dimensions);
700
735
  case accuracy_t::f32_k: return {pun_metric<f32_t>(cos_gt<f32_t>{}), isa_t::auto_k};
701
736
  case accuracy_t::f64_k: return {pun_metric<f64_t>(cos_gt<f64_t>{}), isa_t::auto_k};
702
737
  default: return {};
package/src/bench.cpp CHANGED
@@ -33,7 +33,7 @@
33
33
  #include <variant> // `std::monostate`
34
34
 
35
35
  #include <clipp.h> // Command Line Interface
36
- #include <omp.h> // `omp_get_num_threads()`
36
+ #include <omp.h> // `omp_set_num_threads()`
37
37
 
38
38
  #include <simsimd/simsimd.h>
39
39
 
@@ -400,7 +400,7 @@ struct args_t {
400
400
  bool quantize_f16 = false;
401
401
  bool quantize_f8 = false;
402
402
 
403
- bool metric_ip = true;
403
+ bool metric_ip = false;
404
404
  bool metric_l2 = false;
405
405
  bool metric_cos = false;
406
406
  bool metric_haversine = false;
@@ -408,23 +408,19 @@ struct args_t {
408
408
 
409
409
  template <typename index_at, typename dataset_at> //
410
410
  index_at type_punned_index_for_metric(dataset_at& dataset, args_t const& args, config_t config, accuracy_t accuracy) {
411
- if (args.metric_ip) {
412
- std::printf("-- Metric: Inner Product\n");
413
- return index_at::ip(dataset.dimensions(), accuracy, config);
414
- }
415
411
  if (args.metric_l2) {
416
412
  std::printf("-- Metric: Euclidean\n");
417
413
  return index_at::l2(dataset.dimensions(), accuracy, config);
418
- }
419
- if (args.metric_cos) {
414
+ } else if (args.metric_cos) {
420
415
  std::printf("-- Metric: Angular\n");
421
416
  return index_at::cos(dataset.dimensions(), accuracy, config);
422
- }
423
- if (args.metric_haversine) {
417
+ } else if (args.metric_haversine) {
424
418
  std::printf("-- Metric: Haversine\n");
425
419
  return index_at::haversine(accuracy, config);
420
+ } else {
421
+ std::printf("-- Metric: Inner Product\n");
422
+ return index_at::ip(dataset.dimensions(), accuracy, config);
426
423
  }
427
- throw std::invalid_argument("Unknown metric!");
428
424
  }
429
425
 
430
426
  template <typename index_at, typename dataset_at> //
@@ -472,16 +468,16 @@ template <typename neighbor_id_at, typename dataset_at> //
472
468
  void run_big_or_small(dataset_at& dataset, args_t const& args, config_t config) {
473
469
  if (args.native) {
474
470
  if (args.metric_cos) {
475
- std::printf("-- Metric: Inner Product\n");
476
- run_typed<index_gt<ip_gt<float>, std::size_t, neighbor_id_at>>(dataset, args, config);
471
+ std::printf("-- Metric: Angular\n");
472
+ run_typed<index_gt<cos_gt<float>, std::size_t, neighbor_id_at>>(dataset, args, config);
477
473
  } else if (args.metric_l2) {
478
474
  std::printf("-- Metric: Euclidean\n");
479
475
  run_typed<index_gt<l2sq_gt<float>, std::size_t, neighbor_id_at>>(dataset, args, config);
480
476
  } else if (args.metric_haversine) {
481
- std::printf("-- Metric: Angular\n");
477
+ std::printf("-- Metric: Haversine\n");
482
478
  run_typed<index_gt<haversine_gt<float>, std::size_t, neighbor_id_at>>(dataset, args, config);
483
479
  } else {
484
- std::printf("-- Metric: Haversine\n");
480
+ std::printf("-- Metric: Inner Product\n");
485
481
  run_typed<index_gt<ip_gt<float>, std::size_t, neighbor_id_at>>(dataset, args, config);
486
482
  }
487
483
  } else
@@ -1,82 +0,0 @@
1
- {
2
- "devDependencies": {
3
- "@semantic-release/exec": "github:semantic-release/exec",
4
- "@semantic-release/git": "^10.0.1",
5
- "conventional-changelog-eslint": "^3.0.9",
6
- "semantic-release": "^20.1.3"
7
- },
8
- "release": {
9
- "branches": [
10
- "main"
11
- ],
12
- "debug": true,
13
- "ci": true,
14
- "dryRun": false,
15
- "plugins": [
16
- [
17
- "@semantic-release/commit-analyzer",
18
- {
19
- "preset": "eslint",
20
- "releaseRules": [
21
- {
22
- "tag": "Add",
23
- "release": "minor"
24
- },
25
- {
26
- "tag": "Improve",
27
- "release": "patch"
28
- },
29
- {
30
- "tag": "Make",
31
- "release": "patch"
32
- },
33
- {
34
- "tag": "Refactor",
35
- "release": false
36
- }
37
- ]
38
- }
39
- ],
40
- [
41
- "@semantic-release/release-notes-generator",
42
- {
43
- "preset": "eslint",
44
- "releaseRules": [
45
- {
46
- "tag": "Add",
47
- "release": "minor"
48
- },
49
- {
50
- "tag": "Improve",
51
- "release": "patch"
52
- },
53
- {
54
- "tag": "Make",
55
- "release": "patch"
56
- },
57
- {
58
- "tag": "Refactor",
59
- "release": false
60
- }
61
- ]
62
- }
63
- ],
64
- "@semantic-release/github",
65
- [
66
- "@semantic-release/exec",
67
- {
68
- "prepareCmd": "echo '${nextRelease.version}' > VERSION"
69
- }
70
- ],
71
- [
72
- "@semantic-release/git",
73
- {
74
- "assets": [
75
- "VERSION"
76
- ],
77
- "message": "Build: Released ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
78
- }
79
- ]
80
- ]
81
- }
82
- }
@@ -1,185 +0,0 @@
1
- # Compiling USearch
2
-
3
- ## Build
4
-
5
- Before building the first time, please pull submodules.
6
-
7
- ```sh
8
- git submodule update --init --recursive
9
- ```
10
-
11
- ### Linux
12
-
13
- ```sh
14
- cmake -B ./build_release \
15
- -DCMAKE_CXX_COMPILER="g++-12" \
16
- -DCMAKE_BUILD_TYPE=Release \
17
- -DUSEARCH_USE_SIMD=1 \
18
- -DUSEARCH_USE_OPENMP=1 \
19
- -DUSEARCH_USE_JEMALLOC=1 && \
20
- make -C ./build_release -j
21
-
22
- # To benchmark in C++:
23
- ./build_release/bench \
24
- --vectors datasets/wiki_1M/base.1M.fbin \
25
- --queries datasets/wiki_1M/query.public.100K.fbin \
26
- --neighbors datasets/wiki_1M/groundtruth.public.100K.ibin
27
-
28
- # Large benchmark on a 1TB dataset:
29
- ./build_release/bench \
30
- --vectors datasets/t2i_1B/base.1B.fbin \
31
- --queries datasets/t2i_1B/query.public.100K.fbin \
32
- --neighbors datasets/t2i_1B/groundtruth.public.100K.ibin \
33
- --output datasets/t2i_1B/index.usearch \
34
- --cos
35
-
36
- # To benchmark in Python:
37
- python python/bench.py \
38
- --vectors datasets/wiki_1M/base.1M.fbin \
39
- --queries datasets/wiki_1M/query.public.100K.fbin \
40
- --neighbors datasets/wiki_1M/groundtruth.public.100K.ibin
41
- ```
42
-
43
- ### MacOS
44
-
45
- ```sh
46
- brew install libomp llvm
47
- cmake \
48
- -DCMAKE_C_COMPILER="/opt/homebrew/opt/llvm/bin/clang" \
49
- -DCMAKE_CXX_COMPILER="/opt/homebrew/opt/llvm/bin/clang++" \
50
- -DUSEARCH_USE_SIMD=1 \
51
- -DUSEARCH_USE_OPENMP=1 \
52
- -B ./build_release && \
53
- make -C ./build_release -j
54
- ```
55
-
56
- ### Python
57
-
58
- ```sh
59
- pip install -e .
60
- python python/test.py
61
- ```
62
-
63
- ### JavaScript
64
-
65
- ```sh
66
- npm install
67
- node javascript/test.js
68
- npm publish
69
- ```
70
-
71
- ### Rust
72
-
73
- ```sh
74
- cargo test -p usearch
75
- cargo publish
76
- ```
77
-
78
- ### Java
79
-
80
- ```sh
81
- gradle clean build
82
- java -cp . -Djava.library.path=/Users/av/github/usearch/build/libs/usearch/shared java/cloud/unum/usearch/Index.java
83
- ```
84
-
85
- Or step by-step:
86
-
87
- ```sh
88
- cs java/unum/cloud/usearch
89
- javac -h . Index.java
90
-
91
- # Ubuntu:
92
- g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
93
- g++ -shared -fPIC -o libusearch.so cloud_unum_usearch_Index.o -lc
94
-
95
- # Windows
96
- g++ -c -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
97
- g++ -shared -o USearchJNI.dll cloud_unum_usearch_Index.o -Wl,--add-stdcall-alias
98
-
99
- # MacOS
100
- g++ -std=c++11 -c -fPIC \
101
- -I../../../../include -I../../../../src -I../../../../fp16/include -I../../../../simsimd/include \
102
- -I${JAVA_HOME}/include -I${JAVA_HOME}/include/darwin cloud_unum_usearch_Index.cpp -o cloud_unum_usearch_Index.o
103
- g++ -dynamiclib -o libusearch.dylib cloud_unum_usearch_Index.o -lc
104
- # Run linking to that directory
105
- java -cp . -Djava.library.path=/Users/av/github/usearch/java/cloud/unum/usearch/ Index.java
106
- java -cp . -Djava.library.path=/Users/av/github/usearch/java cloud.unum.usearch.Index
107
- ```
108
-
109
- ### Wolfram
110
-
111
- ```sh
112
- brew install --cask wolfram-engine
113
- ```
114
-
115
- ## Benchmarks
116
-
117
- ### Datasets
118
-
119
- BigANN benchmark is a good starting point, if you are searching for large collections of high-dimensional vectors.
120
- Those often come with precomputed ground-truth neighbors, which is handy for recall evaluation.
121
-
122
- | Dataset | Scalar Type | Dimensions | Metric | Size |
123
- | :-------------------------------------- | :---------: | :--------: | :----: | :-------: |
124
- | [Unum UForm Wiki][unum-wiki] | float32 | 256 | IP | 1 GB |
125
- | [Yandex Text-to-Image Sample][unum-t2i] | float32 | 200 | Cos | 1 GB |
126
- | | | | | |
127
- | [Microsoft SPACEV][spacev] | int8 | 100 | L2 | 93 GB |
128
- | [Microsoft Turing-ANNS][turing] | float32 | 100 | L2 | 373 GB |
129
- | [Yandex Deep1B][deep] | float32 | 96 | L2 | 358 GB |
130
- | [Yandex Text-to-Image][t2i] | float32 | 200 | Cos | 750 GB |
131
- | | | | | |
132
- | [ViT-L/12 LAION][laion] | float32 | 2048 | Cos | 2 - 10 TB |
133
-
134
- Luckily, smaller samples of those datasets are available.
135
-
136
- [unum-wiki]: https://huggingface.co/datasets/unum-cloud/ann-wiki-1m
137
- [unum-t2i]: https://huggingface.co/datasets/unum-cloud/ann-t2i-1m
138
- [spacev]: https://github.com/microsoft/SPTAG/tree/main/datasets/SPACEV1B
139
- [turing]: https://learning2hash.github.io/publications/microsoftturinganns1B/
140
- [t2i]: https://research.yandex.com/blog/benchmarks-for-billion-scale-similarity-search
141
- [deep]: https://research.yandex.com/blog/benchmarks-for-billion-scale-similarity-search
142
- [laion]: https://laion.ai/blog/laion-5b/#download-the-data
143
-
144
- ### Unum UForm Wiki
145
-
146
- ```sh
147
- mkdir -p datasets/wiki_1M/ && \
148
- wget -nc https://huggingface.co/datasets/unum-cloud/ann-wiki-1m/resolve/main/base.1M.fbin -P datasets/wiki_1M/ &&
149
- wget -nc https://huggingface.co/datasets/unum-cloud/ann-wiki-1m/resolve/main/query.public.100K.fbin -P datasets/wiki_1M/ &&
150
- wget -nc https://huggingface.co/datasets/unum-cloud/ann-wiki-1m/resolve/main/groundtruth.public.100K.ibin -P datasets/wiki_1M/
151
- ```
152
-
153
- ### Yandex Text-to-Image
154
-
155
- ```sh
156
- mkdir -p datasets/t2i_1B/ && \
157
- wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/T2I/base.1B.fbin -P datasets/t2i_1B/ &&
158
- wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/T2I/base.1M.fbin -P datasets/t2i_1B/ &&
159
- wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/T2I/query.public.100K.fbin -P datasets/t2i_1B/ &&
160
- wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/T2I/groundtruth.public.100K.ibin -P datasets/t2i_1B/
161
- ```
162
-
163
- ### Yandex Deep1B
164
-
165
- ```sh
166
- mkdir -p datasets/deep_1B/ && \
167
- wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/DEEP/base.1B.fbin -P datasets/deep_1B/ &&
168
- wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/DEEP/base.10M.fbin -P datasets/deep_1B/ &&
169
- wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/DEEP/query.public.10K.fbin -P datasets/deep_1B/ &&
170
- wget -nc https://storage.yandexcloud.net/yandex-research/ann-datasets/DEEP/groundtruth.public.10K.ibin -P datasets/deep_1B/
171
- ```
172
-
173
- ### Profiling
174
-
175
- With `perf`:
176
-
177
- ```sh
178
- # Pass environment variables with `-E`, and `-d` for details
179
- sudo -E perf stat -d ./build_release/bench ...
180
- sudo -E perf mem -d ./build_release/bench ...
181
- # Sample on-CPU functions for the specified command, at 1 Kilo Hertz:
182
- sudo -E perf record -F 1000 ./build_release/bench ...
183
- perf record -d -e arm_spe// -- ./build_release/bench ..
184
- ```
185
-