hnswlib 0.5.0 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec8d1a750f703968418f3aca9beffc33c061c9c5e7e6d24050a8aa8cd15e0d35
4
- data.tar.gz: e8bb3c9b706dfd6d6d25d5f471970b50a6f191efbe9719437d0ce00c0c7a1420
3
+ metadata.gz: dfe29cb17343b0e602684976fa3bd91a9756226e42fdaedded8398e8d97dc83d
4
+ data.tar.gz: 5b1fca956e5a4bb3e28b46dd891f969a4dbfe7b1f03295657322ffc77b089595
5
5
  SHA512:
6
- metadata.gz: 8f39ec12aac12a0af0a4882f092c61b29417d14c575580b821ccb2154568e9c04e6a4170617cfa0185ca32dc322181cd18dd116bd527fc3f61b52cee3180b648
7
- data.tar.gz: e355334afba1ed2f8817e2dda8eedd4bc61670f08ea349e44b7259c01584a195e2bb33f3de9f2f5d4ce59cd6cb80bd17b7f0c3b5e5dedd26dd0d8880c75d25ad
6
+ metadata.gz: 213e43f294b0c6e306aef4a700df4b733b1cbd40253a61d91b15b272b7f0e2b095a5eebc79efb5d5d8af8b01048752a87bad4f610b528d49c1809d09e8e9c51b
7
+ data.tar.gz: 2cffaef5381339dd85d8ad93aff9bf1e3ac271a7797b9e5a8c6122836d3401da17380f31db6594577d5412a0c1372609ed3e3b717c401b603370113d8d811e05
data/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## [0.5.3] - 2022-03-05
2
+
3
+ - Add error handling for std::runtime_error throwed from hnswlib.
4
+ - Add memory release for existing search index when loading index.
5
+
6
+ ## [0.5.2] - 2022-02-19
7
+
8
+ - Update bundled hnswlib version to 0.6.2.
9
+
10
+ ## [0.5.1] - 2022-02-11
11
+
12
+ - Update bundled hnswlib version to 0.6.1.
13
+ - Update documentations.
14
+ - Introduce conventional commits.
15
+
1
16
  ## [0.5.0] - 2021-12-12
2
17
 
3
18
  - Update bundled hnswlib version to 0.6.0.
data/README.md CHANGED
@@ -25,6 +25,20 @@ Or install it yourself as:
25
25
 
26
26
  $ gem install hnswlib
27
27
 
28
+ Note: hnswlib.rb gives no optimization options when building native extensions.
29
+ If necessary, add the optimization options yourself during installation, as follows;
30
+
31
+ ```
32
+ $ bundle config --local build.hnswlib "--with-cxxflags=-march=native"
33
+ $ bundle install
34
+ ```
35
+
36
+ Or:
37
+
38
+ ```
39
+ $ gem install hnswlib -- --with-cxxflags=-march=native
40
+ ```
41
+
28
42
  ## Documentation
29
43
 
30
44
  * [Hnswlib.rb API Documentation](https://yoshoku.github.io/hnswlib.rb/doc/)
@@ -57,4 +71,4 @@ The gem is available as open source under the terms of the [Apache-2.0 License](
57
71
 
58
72
  Bug reports and pull requests are welcome on GitHub at https://github.com/yoshoku/hnswlib.rb.
59
73
  This project is intended to be a safe, welcoming space for collaboration,
60
- and contributors are expected to adhere to the [code of conduct](https://github.com/yoshoku/hnswlib.rb/blob/main/CODE_OF_CONDUCT.md).
74
+ and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * hnswlib.rb is a Ruby binding for the Hnswlib.
3
3
  *
4
- * Copyright (c) 2021 Atsushi Tatsuma
4
+ * Copyright (c) 2021-2022 Atsushi Tatsuma
5
5
  *
6
6
  * Licensed under the Apache License, Version 2.0 (the "License");
7
7
  * you may not use this file except in compliance with the License.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * hnswlib.rb is a Ruby binding for the Hnswlib.
3
3
  *
4
- * Copyright (c) 2021 Atsushi Tatsuma
4
+ * Copyright (c) 2021-2022 Atsushi Tatsuma
5
5
  *
6
6
  * Licensed under the Apache License, Version 2.0 (the "License");
7
7
  * you may not use this file except in compliance with the License.
@@ -277,7 +277,12 @@ class RbHnswlibHierarchicalNSW {
277
277
  const size_t random_seed = (size_t)NUM2INT(kw_values[4]);
278
278
 
279
279
  hnswlib::HierarchicalNSW<float>* ptr = get_hnsw_hierarchicalnsw(self);
280
- new (ptr) hnswlib::HierarchicalNSW<float>(space, max_elements, m, ef_construction, random_seed);
280
+ try {
281
+ new (ptr) hnswlib::HierarchicalNSW<float>(space, max_elements, m, ef_construction, random_seed);
282
+ } catch(const std::runtime_error& e) {
283
+ rb_raise(rb_eRuntimeError, "%s", e.what());
284
+ return Qnil;
285
+ }
281
286
 
282
287
  return Qnil;
283
288
  };
@@ -334,8 +339,14 @@ class RbHnswlibHierarchicalNSW {
334
339
  vec[i] = (float)NUM2DBL(rb_ary_entry(arr, i));
335
340
  }
336
341
 
337
- std::priority_queue<std::pair<float, size_t>> result =
338
- get_hnsw_hierarchicalnsw(self)->searchKnn((void *)vec, (size_t)NUM2INT(k));
342
+ std::priority_queue<std::pair<float, size_t>> result;
343
+ try {
344
+ result = get_hnsw_hierarchicalnsw(self)->searchKnn((void *)vec, (size_t)NUM2INT(k));
345
+ } catch(const std::runtime_error& e) {
346
+ ruby_xfree(vec);
347
+ rb_raise(rb_eRuntimeError, "%s", e.what());
348
+ return Qnil;
349
+ }
339
350
 
340
351
  ruby_xfree(vec);
341
352
 
@@ -376,7 +387,21 @@ class RbHnswlibHierarchicalNSW {
376
387
  } else {
377
388
  space = RbHnswlibInnerProductSpace::get_hnsw_ipspace(ivspace);
378
389
  }
379
- get_hnsw_hierarchicalnsw(self)->loadIndex(filename, space);
390
+ hnswlib::HierarchicalNSW<float>* index = get_hnsw_hierarchicalnsw(self);
391
+ if (index->data_level0_memory_) free(index->data_level0_memory_);
392
+ if (index->linkLists_) {
393
+ for (hnswlib::tableint i = 0; i < index->cur_element_count; i++) {
394
+ if (index->element_levels_[i] > 0 && index->linkLists_[i]) free(index->linkLists_[i]);
395
+ }
396
+ free(index->linkLists_);
397
+ }
398
+ if (index->visited_list_pool_) delete index->visited_list_pool_;
399
+ try {
400
+ index->loadIndex(filename, space);
401
+ } catch(const std::runtime_error& e) {
402
+ rb_raise(rb_eRuntimeError, "%s", e.what());
403
+ return Qnil;
404
+ }
380
405
  RB_GC_GUARD(_filename);
381
406
  return Qnil;
382
407
  };
@@ -389,8 +414,9 @@ class RbHnswlibHierarchicalNSW {
389
414
  for (size_t i = 0; i < vec.size(); i++) {
390
415
  rb_ary_store(ret, i, DBL2NUM((double)vec[i]));
391
416
  }
392
- } catch(std::runtime_error const& e) {
417
+ } catch(const std::runtime_error& e) {
393
418
  rb_raise(rb_eRuntimeError, "%s", e.what());
419
+ return Qnil;
394
420
  }
395
421
  return ret;
396
422
  };
@@ -404,7 +430,12 @@ class RbHnswlibHierarchicalNSW {
404
430
  };
405
431
 
406
432
  static VALUE _hnsw_hierarchicalnsw_mark_deleted(VALUE self, VALUE idx) {
407
- get_hnsw_hierarchicalnsw(self)->markDelete((size_t)NUM2INT(idx));
433
+ try {
434
+ get_hnsw_hierarchicalnsw(self)->markDelete((size_t)NUM2INT(idx));
435
+ } catch(const std::runtime_error& e) {
436
+ rb_raise(rb_eRuntimeError, "%s", e.what());
437
+ return Qnil;
438
+ }
408
439
  return Qnil;
409
440
  };
410
441
 
@@ -413,7 +444,12 @@ class RbHnswlibHierarchicalNSW {
413
444
  rb_raise(rb_eArgError, "Cannot resize, max element is less than the current number of elements.");
414
445
  return Qnil;
415
446
  }
416
- get_hnsw_hierarchicalnsw(self)->resizeIndex((size_t)NUM2INT(new_max_elements));
447
+ try {
448
+ get_hnsw_hierarchicalnsw(self)->resizeIndex((size_t)NUM2INT(new_max_elements));
449
+ } catch(const std::runtime_error& e) {
450
+ rb_raise(rb_eRuntimeError, "%s", e.what());
451
+ return Qnil;
452
+ }
417
453
  return Qnil;
418
454
  };
419
455
 
@@ -510,7 +546,12 @@ class RbHnswlibBruteforceSearch {
510
546
  const size_t max_elements = (size_t)NUM2INT(kw_values[1]);
511
547
 
512
548
  hnswlib::BruteforceSearch<float>* ptr = get_hnsw_bruteforcesearch(self);
513
- new (ptr) hnswlib::BruteforceSearch<float>(space, max_elements);
549
+ try {
550
+ new (ptr) hnswlib::BruteforceSearch<float>(space, max_elements);
551
+ } catch(const std::runtime_error& e) {
552
+ rb_raise(rb_eRuntimeError, "%s", e.what());
553
+ return Qnil;
554
+ }
514
555
 
515
556
  return Qnil;
516
557
  };
@@ -538,7 +579,13 @@ class RbHnswlibBruteforceSearch {
538
579
  vec[i] = (float)NUM2DBL(rb_ary_entry(arr, i));
539
580
  }
540
581
 
541
- get_hnsw_bruteforcesearch(self)->addPoint((void *)vec, (size_t)NUM2INT(idx));
582
+ try {
583
+ get_hnsw_bruteforcesearch(self)->addPoint((void *)vec, (size_t)NUM2INT(idx));
584
+ } catch(const std::runtime_error& e) {
585
+ ruby_xfree(vec);
586
+ rb_raise(rb_eRuntimeError, "%s", e.what());
587
+ return Qfalse;
588
+ }
542
589
 
543
590
  ruby_xfree(vec);
544
591
  return Qtrue;
@@ -609,7 +656,14 @@ class RbHnswlibBruteforceSearch {
609
656
  } else {
610
657
  space = RbHnswlibInnerProductSpace::get_hnsw_ipspace(ivspace);
611
658
  }
612
- get_hnsw_bruteforcesearch(self)->loadIndex(filename, space);
659
+ hnswlib::BruteforceSearch<float>* index = get_hnsw_bruteforcesearch(self);
660
+ if (index->data_) free(index->data_);
661
+ try {
662
+ index->loadIndex(filename, space);
663
+ } catch(const std::runtime_error& e) {
664
+ rb_raise(rb_eRuntimeError, "%s", e.what());
665
+ return Qnil;
666
+ }
613
667
  RB_GC_GUARD(_filename);
614
668
  return Qnil;
615
669
  };
@@ -1004,6 +1004,7 @@ namespace hnswlib {
1004
1004
  unmarkDeletedInternal(existingInternalId);
1005
1005
  }
1006
1006
  updatePoint(data_point, existingInternalId, 1.0);
1007
+
1007
1008
  return existingInternalId;
1008
1009
  }
1009
1010
 
@@ -15,8 +15,25 @@
15
15
  #ifdef _MSC_VER
16
16
  #include <intrin.h>
17
17
  #include <stdexcept>
18
+ #include "cpu_x86.h"
19
+ void cpu_x86::cpuid(int32_t out[4], int32_t eax, int32_t ecx) {
20
+ __cpuidex(out, eax, ecx);
21
+ }
22
+ __int64 xgetbv(unsigned int x) {
23
+ return _xgetbv(x);
24
+ }
18
25
  #else
19
26
  #include <x86intrin.h>
27
+ #include <cpuid.h>
28
+ #include <stdint.h>
29
+ void cpuid(int32_t cpuInfo[4], int32_t eax, int32_t ecx) {
30
+ __cpuid_count(eax, ecx, cpuInfo[0], cpuInfo[1], cpuInfo[2], cpuInfo[3]);
31
+ }
32
+ uint64_t xgetbv(unsigned int index) {
33
+ uint32_t eax, edx;
34
+ __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index));
35
+ return ((uint64_t)edx << 32) | eax;
36
+ }
20
37
  #endif
21
38
 
22
39
  #if defined(USE_AVX512)
@@ -30,6 +47,65 @@
30
47
  #define PORTABLE_ALIGN32 __declspec(align(32))
31
48
  #define PORTABLE_ALIGN64 __declspec(align(64))
32
49
  #endif
50
+
51
+ // Adapted from https://github.com/Mysticial/FeatureDetector
52
+ #define _XCR_XFEATURE_ENABLED_MASK 0
53
+
54
+ bool AVXCapable() {
55
+ int cpuInfo[4];
56
+
57
+ // CPU support
58
+ cpuid(cpuInfo, 0, 0);
59
+ int nIds = cpuInfo[0];
60
+
61
+ bool HW_AVX = false;
62
+ if (nIds >= 0x00000001) {
63
+ cpuid(cpuInfo, 0x00000001, 0);
64
+ HW_AVX = (cpuInfo[2] & ((int)1 << 28)) != 0;
65
+ }
66
+
67
+ // OS support
68
+ cpuid(cpuInfo, 1, 0);
69
+
70
+ bool osUsesXSAVE_XRSTORE = (cpuInfo[2] & (1 << 27)) != 0;
71
+ bool cpuAVXSuport = (cpuInfo[2] & (1 << 28)) != 0;
72
+
73
+ bool avxSupported = false;
74
+ if (osUsesXSAVE_XRSTORE && cpuAVXSuport) {
75
+ uint64_t xcrFeatureMask = xgetbv(_XCR_XFEATURE_ENABLED_MASK);
76
+ avxSupported = (xcrFeatureMask & 0x6) == 0x6;
77
+ }
78
+ return HW_AVX && avxSupported;
79
+ }
80
+
81
+ bool AVX512Capable() {
82
+ if (!AVXCapable()) return false;
83
+
84
+ int cpuInfo[4];
85
+
86
+ // CPU support
87
+ cpuid(cpuInfo, 0, 0);
88
+ int nIds = cpuInfo[0];
89
+
90
+ bool HW_AVX512F = false;
91
+ if (nIds >= 0x00000007) { // AVX512 Foundation
92
+ cpuid(cpuInfo, 0x00000007, 0);
93
+ HW_AVX512F = (cpuInfo[1] & ((int)1 << 16)) != 0;
94
+ }
95
+
96
+ // OS support
97
+ cpuid(cpuInfo, 1, 0);
98
+
99
+ bool osUsesXSAVE_XRSTORE = (cpuInfo[2] & (1 << 27)) != 0;
100
+ bool cpuAVXSuport = (cpuInfo[2] & (1 << 28)) != 0;
101
+
102
+ bool avx512Supported = false;
103
+ if (osUsesXSAVE_XRSTORE && cpuAVXSuport) {
104
+ uint64_t xcrFeatureMask = xgetbv(_XCR_XFEATURE_ENABLED_MASK);
105
+ avx512Supported = (xcrFeatureMask & 0xe6) == 0xe6;
106
+ }
107
+ return HW_AVX512F && avx512Supported;
108
+ }
33
109
  #endif
34
110
 
35
111
  #include <queue>
@@ -108,7 +184,6 @@ namespace hnswlib {
108
184
 
109
185
  return result;
110
186
  }
111
-
112
187
  }
113
188
 
114
189
  #include "space_l2.h"
@@ -10,15 +10,20 @@ namespace hnswlib {
10
10
  for (unsigned i = 0; i < qty; i++) {
11
11
  res += ((float *) pVect1)[i] * ((float *) pVect2)[i];
12
12
  }
13
- return (1.0f - res);
13
+ return res;
14
14
 
15
15
  }
16
16
 
17
+ static float
18
+ InnerProductDistance(const void *pVect1, const void *pVect2, const void *qty_ptr) {
19
+ return 1.0f - InnerProduct(pVect1, pVect2, qty_ptr);
20
+ }
21
+
17
22
  #if defined(USE_AVX)
18
23
 
19
24
  // Favor using AVX if available.
20
25
  static float
21
- InnerProductSIMD4Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
26
+ InnerProductSIMD4ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
22
27
  float PORTABLE_ALIGN32 TmpRes[8];
23
28
  float *pVect1 = (float *) pVect1v;
24
29
  float *pVect2 = (float *) pVect2v;
@@ -61,13 +66,20 @@ namespace hnswlib {
61
66
 
62
67
  _mm_store_ps(TmpRes, sum_prod);
63
68
  float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3];;
64
- return 1.0f - sum;
65
- }
69
+ return sum;
70
+ }
71
+
72
+ static float
73
+ InnerProductDistanceSIMD4ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
74
+ return 1.0f - InnerProductSIMD4ExtAVX(pVect1v, pVect2v, qty_ptr);
75
+ }
66
76
 
67
- #elif defined(USE_SSE)
77
+ #endif
78
+
79
+ #if defined(USE_SSE)
68
80
 
69
81
  static float
70
- InnerProductSIMD4Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
82
+ InnerProductSIMD4ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
71
83
  float PORTABLE_ALIGN32 TmpRes[8];
72
84
  float *pVect1 = (float *) pVect1v;
73
85
  float *pVect2 = (float *) pVect2v;
@@ -119,7 +131,12 @@ namespace hnswlib {
119
131
  _mm_store_ps(TmpRes, sum_prod);
120
132
  float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3];
121
133
 
122
- return 1.0f - sum;
134
+ return sum;
135
+ }
136
+
137
+ static float
138
+ InnerProductDistanceSIMD4ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
139
+ return 1.0f - InnerProductSIMD4ExtSSE(pVect1v, pVect2v, qty_ptr);
123
140
  }
124
141
 
125
142
  #endif
@@ -128,7 +145,7 @@ namespace hnswlib {
128
145
  #if defined(USE_AVX512)
129
146
 
130
147
  static float
131
- InnerProductSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
148
+ InnerProductSIMD16ExtAVX512(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
132
149
  float PORTABLE_ALIGN64 TmpRes[16];
133
150
  float *pVect1 = (float *) pVect1v;
134
151
  float *pVect2 = (float *) pVect2v;
@@ -154,13 +171,20 @@ namespace hnswlib {
154
171
  _mm512_store_ps(TmpRes, sum512);
155
172
  float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] + TmpRes[7] + TmpRes[8] + TmpRes[9] + TmpRes[10] + TmpRes[11] + TmpRes[12] + TmpRes[13] + TmpRes[14] + TmpRes[15];
156
173
 
157
- return 1.0f - sum;
174
+ return sum;
158
175
  }
159
176
 
160
- #elif defined(USE_AVX)
177
+ static float
178
+ InnerProductDistanceSIMD16ExtAVX512(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
179
+ return 1.0f - InnerProductSIMD16ExtAVX512(pVect1v, pVect2v, qty_ptr);
180
+ }
181
+
182
+ #endif
183
+
184
+ #if defined(USE_AVX)
161
185
 
162
186
  static float
163
- InnerProductSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
187
+ InnerProductSIMD16ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
164
188
  float PORTABLE_ALIGN32 TmpRes[8];
165
189
  float *pVect1 = (float *) pVect1v;
166
190
  float *pVect2 = (float *) pVect2v;
@@ -192,13 +216,20 @@ namespace hnswlib {
192
216
  _mm256_store_ps(TmpRes, sum256);
193
217
  float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] + TmpRes[7];
194
218
 
195
- return 1.0f - sum;
219
+ return sum;
220
+ }
221
+
222
+ static float
223
+ InnerProductDistanceSIMD16ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
224
+ return 1.0f - InnerProductSIMD16ExtAVX(pVect1v, pVect2v, qty_ptr);
196
225
  }
197
226
 
198
- #elif defined(USE_SSE)
227
+ #endif
228
+
229
+ #if defined(USE_SSE)
199
230
 
200
- static float
201
- InnerProductSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
231
+ static float
232
+ InnerProductSIMD16ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
202
233
  float PORTABLE_ALIGN32 TmpRes[8];
203
234
  float *pVect1 = (float *) pVect1v;
204
235
  float *pVect2 = (float *) pVect2v;
@@ -239,14 +270,24 @@ namespace hnswlib {
239
270
  _mm_store_ps(TmpRes, sum_prod);
240
271
  float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3];
241
272
 
242
- return 1.0f - sum;
273
+ return sum;
274
+ }
275
+
276
+ static float
277
+ InnerProductDistanceSIMD16ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
278
+ return 1.0f - InnerProductSIMD16ExtSSE(pVect1v, pVect2v, qty_ptr);
243
279
  }
244
280
 
245
281
  #endif
246
282
 
247
283
  #if defined(USE_SSE) || defined(USE_AVX) || defined(USE_AVX512)
284
+ DISTFUNC<float> InnerProductSIMD16Ext = InnerProductSIMD16ExtSSE;
285
+ DISTFUNC<float> InnerProductSIMD4Ext = InnerProductSIMD4ExtSSE;
286
+ DISTFUNC<float> InnerProductDistanceSIMD16Ext = InnerProductDistanceSIMD16ExtSSE;
287
+ DISTFUNC<float> InnerProductDistanceSIMD4Ext = InnerProductDistanceSIMD4ExtSSE;
288
+
248
289
  static float
249
- InnerProductSIMD16ExtResiduals(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
290
+ InnerProductDistanceSIMD16ExtResiduals(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
250
291
  size_t qty = *((size_t *) qty_ptr);
251
292
  size_t qty16 = qty >> 4 << 4;
252
293
  float res = InnerProductSIMD16Ext(pVect1v, pVect2v, &qty16);
@@ -255,11 +296,11 @@ namespace hnswlib {
255
296
 
256
297
  size_t qty_left = qty - qty16;
257
298
  float res_tail = InnerProduct(pVect1, pVect2, &qty_left);
258
- return res + res_tail - 1.0f;
299
+ return 1.0f - (res + res_tail);
259
300
  }
260
301
 
261
302
  static float
262
- InnerProductSIMD4ExtResiduals(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
303
+ InnerProductDistanceSIMD4ExtResiduals(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
263
304
  size_t qty = *((size_t *) qty_ptr);
264
305
  size_t qty4 = qty >> 2 << 2;
265
306
 
@@ -270,7 +311,7 @@ namespace hnswlib {
270
311
  float *pVect2 = (float *) pVect2v + qty4;
271
312
  float res_tail = InnerProduct(pVect1, pVect2, &qty_left);
272
313
 
273
- return res + res_tail - 1.0f;
314
+ return 1.0f - (res + res_tail);
274
315
  }
275
316
  #endif
276
317
 
@@ -282,16 +323,37 @@ namespace hnswlib {
282
323
  public:
283
324
  InnerProductSpace() : data_size_(0), dim_(0) { }
284
325
  InnerProductSpace(size_t dim) {
285
- fstdistfunc_ = InnerProduct;
326
+ fstdistfunc_ = InnerProductDistance;
286
327
  #if defined(USE_AVX) || defined(USE_SSE) || defined(USE_AVX512)
328
+ #if defined(USE_AVX512)
329
+ if (AVX512Capable()) {
330
+ InnerProductSIMD16Ext = InnerProductSIMD16ExtAVX512;
331
+ InnerProductDistanceSIMD16Ext = InnerProductDistanceSIMD16ExtAVX512;
332
+ } else if (AVXCapable()) {
333
+ InnerProductSIMD16Ext = InnerProductSIMD16ExtAVX;
334
+ InnerProductDistanceSIMD16Ext = InnerProductDistanceSIMD16ExtAVX;
335
+ }
336
+ #elif defined(USE_AVX)
337
+ if (AVXCapable()) {
338
+ InnerProductSIMD16Ext = InnerProductSIMD16ExtAVX;
339
+ InnerProductDistanceSIMD16Ext = InnerProductDistanceSIMD16ExtAVX;
340
+ }
341
+ #endif
342
+ #if defined(USE_AVX)
343
+ if (AVXCapable()) {
344
+ InnerProductSIMD4Ext = InnerProductSIMD4ExtAVX;
345
+ InnerProductDistanceSIMD4Ext = InnerProductDistanceSIMD4ExtAVX;
346
+ }
347
+ #endif
348
+
287
349
  if (dim % 16 == 0)
288
- fstdistfunc_ = InnerProductSIMD16Ext;
350
+ fstdistfunc_ = InnerProductDistanceSIMD16Ext;
289
351
  else if (dim % 4 == 0)
290
- fstdistfunc_ = InnerProductSIMD4Ext;
352
+ fstdistfunc_ = InnerProductDistanceSIMD4Ext;
291
353
  else if (dim > 16)
292
- fstdistfunc_ = InnerProductSIMD16ExtResiduals;
354
+ fstdistfunc_ = InnerProductDistanceSIMD16ExtResiduals;
293
355
  else if (dim > 4)
294
- fstdistfunc_ = InnerProductSIMD4ExtResiduals;
356
+ fstdistfunc_ = InnerProductDistanceSIMD4ExtResiduals;
295
357
  #endif
296
358
  dim_ = dim;
297
359
  data_size_ = dim * sizeof(float);
@@ -312,5 +374,4 @@ namespace hnswlib {
312
374
  ~InnerProductSpace() {}
313
375
  };
314
376
 
315
-
316
377
  }
@@ -23,7 +23,7 @@ namespace hnswlib {
23
23
 
24
24
  // Favor using AVX512 if available.
25
25
  static float
26
- L2SqrSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
26
+ L2SqrSIMD16ExtAVX512(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
27
27
  float *pVect1 = (float *) pVect1v;
28
28
  float *pVect2 = (float *) pVect2v;
29
29
  size_t qty = *((size_t *) qty_ptr);
@@ -52,12 +52,13 @@ namespace hnswlib {
52
52
 
53
53
  return (res);
54
54
  }
55
+ #endif
55
56
 
56
- #elif defined(USE_AVX)
57
+ #if defined(USE_AVX)
57
58
 
58
59
  // Favor using AVX if available.
59
60
  static float
60
- L2SqrSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
61
+ L2SqrSIMD16ExtAVX(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
61
62
  float *pVect1 = (float *) pVect1v;
62
63
  float *pVect2 = (float *) pVect2v;
63
64
  size_t qty = *((size_t *) qty_ptr);
@@ -89,10 +90,12 @@ namespace hnswlib {
89
90
  return TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] + TmpRes[7];
90
91
  }
91
92
 
92
- #elif defined(USE_SSE)
93
+ #endif
94
+
95
+ #if defined(USE_SSE)
93
96
 
94
97
  static float
95
- L2SqrSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
98
+ L2SqrSIMD16ExtSSE(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
96
99
  float *pVect1 = (float *) pVect1v;
97
100
  float *pVect2 = (float *) pVect2v;
98
101
  size_t qty = *((size_t *) qty_ptr);
@@ -141,6 +144,8 @@ namespace hnswlib {
141
144
  #endif
142
145
 
143
146
  #if defined(USE_SSE) || defined(USE_AVX) || defined(USE_AVX512)
147
+ DISTFUNC<float> L2SqrSIMD16Ext = L2SqrSIMD16ExtSSE;
148
+
144
149
  static float
145
150
  L2SqrSIMD16ExtResiduals(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
146
151
  size_t qty = *((size_t *) qty_ptr);
@@ -156,7 +161,7 @@ namespace hnswlib {
156
161
  #endif
157
162
 
158
163
 
159
- #ifdef USE_SSE
164
+ #if defined(USE_SSE)
160
165
  static float
161
166
  L2SqrSIMD4Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
162
167
  float PORTABLE_ALIGN32 TmpRes[8];
@@ -209,7 +214,17 @@ namespace hnswlib {
209
214
  L2Space() : data_size_(0), dim_(0) { }
210
215
  L2Space(size_t dim) {
211
216
  fstdistfunc_ = L2Sqr;
212
- #if defined(USE_SSE) || defined(USE_AVX) || defined(USE_AVX512)
217
+ #if defined(USE_SSE) || defined(USE_AVX) || defined(USE_AVX512)
218
+ #if defined(USE_AVX512)
219
+ if (AVX512Capable())
220
+ L2SqrSIMD16Ext = L2SqrSIMD16ExtAVX512;
221
+ else if (AVXCapable())
222
+ L2SqrSIMD16Ext = L2SqrSIMD16ExtAVX;
223
+ #elif defined(USE_AVX)
224
+ if (AVXCapable())
225
+ L2SqrSIMD16Ext = L2SqrSIMD16ExtAVX;
226
+ #endif
227
+
213
228
  if (dim % 16 == 0)
214
229
  fstdistfunc_ = L2SqrSIMD16Ext;
215
230
  else if (dim % 4 == 0)
@@ -218,7 +233,7 @@ namespace hnswlib {
218
233
  fstdistfunc_ = L2SqrSIMD16ExtResiduals;
219
234
  else if (dim > 4)
220
235
  fstdistfunc_ = L2SqrSIMD4ExtResiduals;
221
- #endif
236
+ #endif
222
237
  dim_ = dim;
223
238
  data_size_ = dim * sizeof(float);
224
239
  }
@@ -3,8 +3,8 @@
3
3
  # Hnswlib.rb provides Ruby bindings for the Hnswlib.
4
4
  module Hnswlib
5
5
  # The version of Hnswlib.rb you install.
6
- VERSION = '0.5.0'
6
+ VERSION = '0.5.3'
7
7
 
8
8
  # The version of Hnswlib included with gem.
9
- HSWLIB_VERSION = '0.6.0'
9
+ HSWLIB_VERSION = '0.6.2'
10
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hnswlib
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - yoshoku
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-11 00:00:00.000000000 Z
11
+ date: 2022-03-05 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Hnswlib.rb provides Ruby bindings for the Hnswlib.
14
14
  email:
@@ -18,16 +18,9 @@ extensions:
18
18
  - ext/hnswlib/extconf.rb
19
19
  extra_rdoc_files: []
20
20
  files:
21
- - ".github/workflows/build.yml"
22
- - ".gitignore"
23
- - ".rspec"
24
21
  - CHANGELOG.md
25
- - CODE_OF_CONDUCT.md
26
- - Gemfile
27
22
  - LICENSE.txt
28
23
  - README.md
29
- - Rakefile
30
- - Steepfile
31
24
  - ext/hnswlib/extconf.rb
32
25
  - ext/hnswlib/hnswlibext.cpp
33
26
  - ext/hnswlib/hnswlibext.hpp
@@ -38,7 +31,6 @@ files:
38
31
  - ext/hnswlib/src/space_ip.h
39
32
  - ext/hnswlib/src/space_l2.h
40
33
  - ext/hnswlib/src/visited_list_pool.h
41
- - hnswlib.gemspec
42
34
  - lib/hnswlib.rb
43
35
  - lib/hnswlib/version.rb
44
36
  - sig/hnswlib.rbs
@@ -64,7 +56,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
64
56
  - !ruby/object:Gem::Version
65
57
  version: '0'
66
58
  requirements: []
67
- rubygems_version: 3.2.32
59
+ rubygems_version: 3.3.7
68
60
  signing_key:
69
61
  specification_version: 4
70
62
  summary: Ruby bindings for the Hnswlib.
@@ -1,20 +0,0 @@
1
- name: build
2
-
3
- on: [push, pull_request]
4
-
5
- jobs:
6
- build:
7
- runs-on: ubuntu-latest
8
- strategy:
9
- fail-fast: false
10
- matrix:
11
- ruby: [ '2.6', '2.7', '3.0' ]
12
- steps:
13
- - uses: actions/checkout@v2
14
- - name: Set up Ruby ${{ matrix.ruby }}
15
- uses: ruby/setup-ruby@v1
16
- with:
17
- ruby-version: ${{ matrix.ruby }}
18
- bundler-cache: true
19
- - name: Build and test with Rake
20
- run: bundle exec rake
data/.gitignore DELETED
@@ -1,19 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
- *.bundle
10
- *.so
11
- *.o
12
- *.a
13
- mkmf.log
14
-
15
- # rspec failure tracking
16
- .rspec_status
17
-
18
- *.ann
19
- /bin/
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
data/CODE_OF_CONDUCT.md DELETED
@@ -1,84 +0,0 @@
1
- # Contributor Covenant Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
-
7
- We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
-
9
- ## Our Standards
10
-
11
- Examples of behavior that contributes to a positive environment for our community include:
12
-
13
- * Demonstrating empathy and kindness toward other people
14
- * Being respectful of differing opinions, viewpoints, and experiences
15
- * Giving and gracefully accepting constructive feedback
16
- * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17
- * Focusing on what is best not just for us as individuals, but for the overall community
18
-
19
- Examples of unacceptable behavior include:
20
-
21
- * The use of sexualized language or imagery, and sexual attention or
22
- advances of any kind
23
- * Trolling, insulting or derogatory comments, and personal or political attacks
24
- * Public or private harassment
25
- * Publishing others' private information, such as a physical or email
26
- address, without their explicit permission
27
- * Other conduct which could reasonably be considered inappropriate in a
28
- professional setting
29
-
30
- ## Enforcement Responsibilities
31
-
32
- Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
33
-
34
- Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
35
-
36
- ## Scope
37
-
38
- This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
39
-
40
- ## Enforcement
41
-
42
- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at yoshoku@outlook.com. All complaints will be reviewed and investigated promptly and fairly.
43
-
44
- All community leaders are obligated to respect the privacy and security of the reporter of any incident.
45
-
46
- ## Enforcement Guidelines
47
-
48
- Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
49
-
50
- ### 1. Correction
51
-
52
- **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
53
-
54
- **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
55
-
56
- ### 2. Warning
57
-
58
- **Community Impact**: A violation through a single incident or series of actions.
59
-
60
- **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
61
-
62
- ### 3. Temporary Ban
63
-
64
- **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
65
-
66
- **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
67
-
68
- ### 4. Permanent Ban
69
-
70
- **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
71
-
72
- **Consequence**: A permanent ban from any sort of public interaction within the community.
73
-
74
- ## Attribution
75
-
76
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
77
- available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
78
-
79
- Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
80
-
81
- [homepage]: https://www.contributor-covenant.org
82
-
83
- For answers to common questions about this code of conduct, see the FAQ at
84
- https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
data/Gemfile DELETED
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- # Specify your gem's dependencies in hnswlib.gemspec
6
- gemspec
7
-
8
- gem 'rake', '~> 13.0'
9
- gem 'rake-compiler', '~> 1.1'
10
- gem 'rspec', '~> 3.0'
11
- gem 'rbs', '~> 1.2'
12
- gem 'steep', '~> 0.44'
data/Rakefile DELETED
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
4
- require 'rspec/core/rake_task'
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- require 'rake/extensiontask'
9
-
10
- task build: :compile
11
-
12
- Rake::ExtensionTask.new('hnswlibext') do |ext|
13
- ext.ext_dir = 'ext/hnswlib'
14
- ext.lib_dir = 'lib/hnswlib'
15
- end
16
-
17
- task default: %i[clobber compile spec]
data/Steepfile DELETED
@@ -1,27 +0,0 @@
1
- # D = Steep::Diagnostic
2
- #
3
- target :lib do
4
- signature "sig"
5
- #
6
- check "lib" # Directory name
7
- # check "Gemfile" # File name
8
- # check "app/models/**/*.rb" # Glob
9
- # # ignore "lib/templates/*.rb"
10
- #
11
- # # library "pathname", "set" # Standard libraries
12
- # # library "strong_json" # Gems
13
- #
14
- # # configure_code_diagnostics(D::Ruby.strict) # `strict` diagnostics setting
15
- # # configure_code_diagnostics(D::Ruby.lenient) # `lenient` diagnostics setting
16
- # # configure_code_diagnostics do |hash| # You can setup everything yourself
17
- # # hash[D::Ruby::NoMethod] = :information
18
- # # end
19
- end
20
-
21
- # target :test do
22
- # signature "sig", "sig-private"
23
- #
24
- # check "test"
25
- #
26
- # # library "pathname", "set" # Standard libraries
27
- # end
data/hnswlib.gemspec DELETED
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'lib/hnswlib/version'
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = 'hnswlib'
7
- spec.version = Hnswlib::VERSION
8
- spec.authors = ['yoshoku']
9
- spec.email = ['yoshoku@outlook.com']
10
-
11
- spec.summary = 'Ruby bindings for the Hnswlib.'
12
- spec.description = 'Hnswlib.rb provides Ruby bindings for the Hnswlib.'
13
- spec.homepage = 'https://github.com/yoshoku/hnswlib.rb'
14
- spec.license = 'Apache-2.0'
15
-
16
- spec.metadata['homepage_uri'] = spec.homepage
17
- spec.metadata['source_code_uri'] = spec.homepage
18
- spec.metadata['changelog_uri'] = 'https://github.com/yoshoku/hnswlib.rb/blob/main/CHANGELOG.md'
19
-
20
- # Specify which files should be added to the gem when it is released.
21
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }.reject { |f| f.include?('dummy.rb') }
24
- end
25
- spec.bindir = 'exe'
26
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
27
- spec.require_paths = ['lib']
28
- spec.extensions = ['ext/hnswlib/extconf.rb']
29
-
30
- # Uncomment to register a new dependency of your gem
31
- # spec.add_dependency "example-gem", "~> 1.0"
32
-
33
- # For more information and examples about making a new gem, checkout our
34
- # guide at: https://bundler.io/guides/creating_gem.html
35
- end