hnswlib 0.4.0 → 0.5.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 99f5f1403a51083df75ef842a11b996fe6c159f95c3798f217a076cb5f535254
4
- data.tar.gz: f9972ac4e644727a126e937e81494cc11bc051bc7ff0daf57139845aed1e7719
3
+ metadata.gz: ec8d1a750f703968418f3aca9beffc33c061c9c5e7e6d24050a8aa8cd15e0d35
4
+ data.tar.gz: e8bb3c9b706dfd6d6d25d5f471970b50a6f191efbe9719437d0ce00c0c7a1420
5
5
  SHA512:
6
- metadata.gz: c4f96fceab228e3cffd9f2b77e513b86fe71ec3156cdff10c0bc9206ad7951f87c507531088f91f45a940c399102bff6a2dedc5fee8b09b37a5f5739e9a02b6c
7
- data.tar.gz: 8297e1e0d1f6753b3e8900b07b80dba271276a14303b102cd8084e480ec491da269baac1fc8be08ab350976fc22cb859583745dc219d73d6f56b33fe7502ac2b
6
+ metadata.gz: 8f39ec12aac12a0af0a4882f092c61b29417d14c575580b821ccb2154568e9c04e6a4170617cfa0185ca32dc322181cd18dd116bd527fc3f61b52cee3180b648
7
+ data.tar.gz: e355334afba1ed2f8817e2dda8eedd4bc61670f08ea349e44b7259c01584a195e2bb33f3de9f2f5d4ce59cd6cb80bd17b7f0c3b5e5dedd26dd0d8880c75d25ad
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## [0.5.0] - 2021-12-12
2
+
3
+ - Update bundled hnswlib version to 0.6.0.
4
+
1
5
  ## [0.4.0] - 2021-09-12
2
6
 
3
7
  - Add type declaration file.
@@ -92,13 +92,13 @@ namespace hnswlib {
92
92
  searchKnn(const void *query_data, size_t k) const {
93
93
  std::priority_queue<std::pair<dist_t, labeltype >> topResults;
94
94
  if (cur_element_count == 0) return topResults;
95
- for (size_t i = 0; i < k; i++) {
95
+ for (int i = 0; i < k; i++) {
96
96
  dist_t dist = fstdistfunc_(query_data, data_ + size_per_element_ * i, dist_func_param_);
97
97
  topResults.push(std::pair<dist_t, labeltype>(dist, *((labeltype *) (data_ + size_per_element_ * i +
98
98
  data_size_))));
99
99
  }
100
100
  dist_t lastdist = topResults.top().first;
101
- for (size_t i = k; i < cur_element_count; i++) {
101
+ for (int i = k; i < cur_element_count; i++) {
102
102
  dist_t dist = fstdistfunc_(query_data, data_ + size_per_element_ * i, dist_func_param_);
103
103
  if (dist <= lastdist) {
104
104
  topResults.push(std::pair<dist_t, labeltype>(dist, *((labeltype *) (data_ + size_per_element_ * i +
@@ -19,7 +19,6 @@ namespace hnswlib {
19
19
  static const tableint max_update_element_locks = 65536;
20
20
  HierarchicalNSW() : visited_list_pool_(nullptr), data_level0_memory_(nullptr), linkLists_(nullptr), cur_element_count(0) { }
21
21
  HierarchicalNSW(SpaceInterface<dist_t> *s) {
22
-
23
22
  }
24
23
 
25
24
  HierarchicalNSW(SpaceInterface<dist_t> *s, const std::string &location, bool nmslib = false, size_t max_elements=0) {
@@ -30,7 +29,7 @@ namespace hnswlib {
30
29
  link_list_locks_(max_elements), link_list_update_locks_(max_update_element_locks), element_levels_(max_elements) {
31
30
  max_elements_ = max_elements;
32
31
 
33
- has_deletions_=false;
32
+ num_deleted_ = 0;
34
33
  data_size_ = s->get_data_size();
35
34
  fstdistfunc_ = s->get_dist_func();
36
35
  dist_func_param_ = s->get_dist_func_param();
@@ -57,8 +56,6 @@ namespace hnswlib {
57
56
 
58
57
  visited_list_pool_ = new VisitedListPool(1, max_elements);
59
58
 
60
-
61
-
62
59
  //initializations for special treatment of the first node
63
60
  enterpoint_node_ = -1;
64
61
  maxlevel_ = -1;
@@ -93,6 +90,7 @@ namespace hnswlib {
93
90
  size_t cur_element_count;
94
91
  size_t size_data_per_element_;
95
92
  size_t size_links_per_element_;
93
+ size_t num_deleted_;
96
94
 
97
95
  size_t M_;
98
96
  size_t maxM_;
@@ -113,20 +111,15 @@ namespace hnswlib {
113
111
  std::vector<std::mutex> link_list_update_locks_;
114
112
  tableint enterpoint_node_;
115
113
 
116
-
117
114
  size_t size_links_level0_;
118
115
  size_t offsetData_, offsetLevel0_;
119
116
 
120
-
121
117
  char *data_level0_memory_;
122
118
  char **linkLists_;
123
119
  std::vector<int> element_levels_;
124
120
 
125
121
  size_t data_size_;
126
122
 
127
- bool has_deletions_;
128
-
129
-
130
123
  size_t label_offset_;
131
124
  DISTFUNC<dist_t> fstdistfunc_;
132
125
  void *dist_func_param_;
@@ -183,7 +176,7 @@ namespace hnswlib {
183
176
 
184
177
  while (!candidateSet.empty()) {
185
178
  std::pair<dist_t, tableint> curr_el_pair = candidateSet.top();
186
- if ((-curr_el_pair.first) > lowerBound) {
179
+ if ((-curr_el_pair.first) > lowerBound && top_candidates.size() == ef_construction_) {
187
180
  break;
188
181
  }
189
182
  candidateSet.pop();
@@ -272,7 +265,7 @@ namespace hnswlib {
272
265
 
273
266
  std::pair<dist_t, tableint> current_node_pair = candidate_set.top();
274
267
 
275
- if ((-current_node_pair.first) > lowerBound) {
268
+ if ((-current_node_pair.first) > lowerBound && (top_candidates.size() == ef || has_deletions == false)) {
276
269
  break;
277
270
  }
278
271
  candidate_set.pop();
@@ -548,7 +541,7 @@ namespace hnswlib {
548
541
  }
549
542
  }
550
543
 
551
- if (has_deletions_) {
544
+ if (num_deleted_) {
552
545
  std::priority_queue<std::pair<dist_t, tableint >> top_candidates1=searchBaseLayerST<true>(currObj, query_data,
553
546
  ef_);
554
547
  top_candidates.swap(top_candidates1);
@@ -624,8 +617,6 @@ namespace hnswlib {
624
617
  }
625
618
 
626
619
  void loadIndex(const std::string &location, SpaceInterface<dist_t> *s, size_t max_elements_i=0) {
627
-
628
-
629
620
  std::ifstream input(location, std::ios::binary);
630
621
 
631
622
  if (!input.is_open())
@@ -640,7 +631,7 @@ namespace hnswlib {
640
631
  readBinaryPOD(input, max_elements_);
641
632
  readBinaryPOD(input, cur_element_count);
642
633
 
643
- size_t max_elements=max_elements_i;
634
+ size_t max_elements = max_elements_i;
644
635
  if(max_elements < cur_element_count)
645
636
  max_elements = max_elements_;
646
637
  max_elements_ = max_elements;
@@ -689,26 +680,19 @@ namespace hnswlib {
689
680
 
690
681
  input.seekg(pos,input.beg);
691
682
 
692
-
693
683
  data_level0_memory_ = (char *) malloc(max_elements * size_data_per_element_);
694
684
  if (data_level0_memory_ == nullptr)
695
685
  throw std::runtime_error("Not enough memory: loadIndex failed to allocate level0");
696
686
  input.read(data_level0_memory_, cur_element_count * size_data_per_element_);
697
687
 
698
-
699
-
700
-
701
688
  size_links_per_element_ = maxM_ * sizeof(tableint) + sizeof(linklistsizeint);
702
689
 
703
-
704
690
  size_links_level0_ = maxM0_ * sizeof(tableint) + sizeof(linklistsizeint);
705
691
  std::vector<std::mutex>(max_elements).swap(link_list_locks_);
706
692
  std::vector<std::mutex>(max_update_element_locks).swap(link_list_update_locks_);
707
693
 
708
-
709
694
  visited_list_pool_ = new VisitedListPool(1, max_elements);
710
695
 
711
-
712
696
  linkLists_ = (char **) malloc(sizeof(void *) * max_elements);
713
697
  if (linkLists_ == nullptr)
714
698
  throw std::runtime_error("Not enough memory: loadIndex failed to allocate linklists");
@@ -732,11 +716,9 @@ namespace hnswlib {
732
716
  }
733
717
  }
734
718
 
735
- has_deletions_=false;
736
-
737
719
  for (size_t i = 0; i < cur_element_count; i++) {
738
720
  if(isMarkedDeleted(i))
739
- has_deletions_=true;
721
+ num_deleted_ += 1;
740
722
  }
741
723
 
742
724
  input.close();
@@ -745,7 +727,7 @@ namespace hnswlib {
745
727
  }
746
728
 
747
729
  template<typename data_t>
748
- std::vector<data_t> getDataByLabel(labeltype label)
730
+ std::vector<data_t> getDataByLabel(labeltype label) const
749
731
  {
750
732
  tableint label_c;
751
733
  auto search = label_lookup_.find(label);
@@ -758,7 +740,7 @@ namespace hnswlib {
758
740
  size_t dim = *((size_t *) dist_func_param_);
759
741
  std::vector<data_t> data;
760
742
  data_t* data_ptr = (data_t*) data_ptrv;
761
- for (size_t i = 0; i < dim; i++) {
743
+ for (int i = 0; i < dim; i++) {
762
744
  data.push_back(*data_ptr);
763
745
  data_ptr += 1;
764
746
  }
@@ -766,19 +748,19 @@ namespace hnswlib {
766
748
  }
767
749
 
768
750
  static const unsigned char DELETE_MARK = 0x01;
769
- // static const unsigned char REUSE_MARK = 0x10;
751
+ // static const unsigned char REUSE_MARK = 0x10;
770
752
  /**
771
753
  * Marks an element with the given label deleted, does NOT really change the current graph.
772
754
  * @param label
773
755
  */
774
756
  void markDelete(labeltype label)
775
757
  {
776
- has_deletions_=true;
777
758
  auto search = label_lookup_.find(label);
778
759
  if (search == label_lookup_.end()) {
779
760
  throw std::runtime_error("Label not found");
780
761
  }
781
- markDeletedInternal(search->second);
762
+ tableint internalId = search->second;
763
+ markDeletedInternal(internalId);
782
764
  }
783
765
 
784
766
  /**
@@ -787,8 +769,31 @@ namespace hnswlib {
787
769
  * @param internalId
788
770
  */
789
771
  void markDeletedInternal(tableint internalId) {
790
- unsigned char *ll_cur = ((unsigned char *)get_linklist0(internalId))+2;
791
- *ll_cur |= DELETE_MARK;
772
+ assert(internalId < cur_element_count);
773
+ if (!isMarkedDeleted(internalId))
774
+ {
775
+ unsigned char *ll_cur = ((unsigned char *)get_linklist0(internalId))+2;
776
+ *ll_cur |= DELETE_MARK;
777
+ num_deleted_ += 1;
778
+ }
779
+ else
780
+ {
781
+ throw std::runtime_error("The requested to delete element is already deleted");
782
+ }
783
+ }
784
+
785
+ /**
786
+ * Remove the deleted mark of the node, does NOT really change the current graph.
787
+ * @param label
788
+ */
789
+ void unmarkDelete(labeltype label)
790
+ {
791
+ auto search = label_lookup_.find(label);
792
+ if (search == label_lookup_.end()) {
793
+ throw std::runtime_error("Label not found");
794
+ }
795
+ tableint internalId = search->second;
796
+ unmarkDeletedInternal(internalId);
792
797
  }
793
798
 
794
799
  /**
@@ -796,8 +801,17 @@ namespace hnswlib {
796
801
  * @param internalId
797
802
  */
798
803
  void unmarkDeletedInternal(tableint internalId) {
799
- unsigned char *ll_cur = ((unsigned char *)get_linklist0(internalId))+2;
800
- *ll_cur &= ~DELETE_MARK;
804
+ assert(internalId < cur_element_count);
805
+ if (isMarkedDeleted(internalId))
806
+ {
807
+ unsigned char *ll_cur = ((unsigned char *)get_linklist0(internalId))+2;
808
+ *ll_cur &= ~DELETE_MARK;
809
+ num_deleted_ -= 1;
810
+ }
811
+ else
812
+ {
813
+ throw std::runtime_error("The requested to undelete element is not deleted");
814
+ }
801
815
  }
802
816
 
803
817
  /**
@@ -858,8 +872,8 @@ namespace hnswlib {
858
872
  }
859
873
 
860
874
  for (auto&& neigh : sNeigh) {
861
- // if (neigh == internalId)
862
- // continue;
875
+ // if (neigh == internalId)
876
+ // continue;
863
877
 
864
878
  std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> candidates;
865
879
  size_t size = sCand.find(neigh) == sCand.end() ? sCand.size() : sCand.size() - 1; // sCand guaranteed to have size >= 1
@@ -990,7 +1004,6 @@ namespace hnswlib {
990
1004
  unmarkDeletedInternal(existingInternalId);
991
1005
  }
992
1006
  updatePoint(data_point, existingInternalId, 1.0);
993
-
994
1007
  return existingInternalId;
995
1008
  }
996
1009
 
@@ -1134,7 +1147,7 @@ namespace hnswlib {
1134
1147
  }
1135
1148
 
1136
1149
  std::priority_queue<std::pair<dist_t, tableint>, std::vector<std::pair<dist_t, tableint>>, CompareByFirst> top_candidates;
1137
- if (has_deletions_) {
1150
+ if (num_deleted_) {
1138
1151
  top_candidates=searchBaseLayerST<true,true>(
1139
1152
  currObj, query_data, std::max(ef_, k));
1140
1153
  }
@@ -4,6 +4,9 @@
4
4
  #define USE_SSE
5
5
  #ifdef __AVX__
6
6
  #define USE_AVX
7
+ #ifdef __AVX512F__
8
+ #define USE_AVX512
9
+ #endif
7
10
  #endif
8
11
  #endif
9
12
  #endif
@@ -16,10 +19,16 @@
16
19
  #include <x86intrin.h>
17
20
  #endif
18
21
 
22
+ #if defined(USE_AVX512)
23
+ #include <immintrin.h>
24
+ #endif
25
+
19
26
  #if defined(__GNUC__)
20
27
  #define PORTABLE_ALIGN32 __attribute__((aligned(32)))
28
+ #define PORTABLE_ALIGN64 __attribute__((aligned(64)))
21
29
  #else
22
30
  #define PORTABLE_ALIGN32 __declspec(align(32))
31
+ #define PORTABLE_ALIGN64 __declspec(align(64))
23
32
  #endif
24
33
  #endif
25
34
 
@@ -124,7 +124,40 @@ namespace hnswlib {
124
124
 
125
125
  #endif
126
126
 
127
- #if defined(USE_AVX)
127
+
128
+ #if defined(USE_AVX512)
129
+
130
+ static float
131
+ InnerProductSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
132
+ float PORTABLE_ALIGN64 TmpRes[16];
133
+ float *pVect1 = (float *) pVect1v;
134
+ float *pVect2 = (float *) pVect2v;
135
+ size_t qty = *((size_t *) qty_ptr);
136
+
137
+ size_t qty16 = qty / 16;
138
+
139
+
140
+ const float *pEnd1 = pVect1 + 16 * qty16;
141
+
142
+ __m512 sum512 = _mm512_set1_ps(0);
143
+
144
+ while (pVect1 < pEnd1) {
145
+ //_mm_prefetch((char*)(pVect2 + 16), _MM_HINT_T0);
146
+
147
+ __m512 v1 = _mm512_loadu_ps(pVect1);
148
+ pVect1 += 16;
149
+ __m512 v2 = _mm512_loadu_ps(pVect2);
150
+ pVect2 += 16;
151
+ sum512 = _mm512_add_ps(sum512, _mm512_mul_ps(v1, v2));
152
+ }
153
+
154
+ _mm512_store_ps(TmpRes, sum512);
155
+ 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
+
157
+ return 1.0f - sum;
158
+ }
159
+
160
+ #elif defined(USE_AVX)
128
161
 
129
162
  static float
130
163
  InnerProductSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
@@ -211,7 +244,7 @@ namespace hnswlib {
211
244
 
212
245
  #endif
213
246
 
214
- #if defined(USE_SSE) || defined(USE_AVX)
247
+ #if defined(USE_SSE) || defined(USE_AVX) || defined(USE_AVX512)
215
248
  static float
216
249
  InnerProductSIMD16ExtResiduals(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
217
250
  size_t qty = *((size_t *) qty_ptr);
@@ -250,7 +283,7 @@ namespace hnswlib {
250
283
  InnerProductSpace() : data_size_(0), dim_(0) { }
251
284
  InnerProductSpace(size_t dim) {
252
285
  fstdistfunc_ = InnerProduct;
253
- #if defined(USE_AVX) || defined(USE_SSE)
286
+ #if defined(USE_AVX) || defined(USE_SSE) || defined(USE_AVX512)
254
287
  if (dim % 16 == 0)
255
288
  fstdistfunc_ = InnerProductSIMD16Ext;
256
289
  else if (dim % 4 == 0)
@@ -19,7 +19,41 @@ namespace hnswlib {
19
19
  return (res);
20
20
  }
21
21
 
22
- #if defined(USE_AVX)
22
+ #if defined(USE_AVX512)
23
+
24
+ // Favor using AVX512 if available.
25
+ static float
26
+ L2SqrSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
27
+ float *pVect1 = (float *) pVect1v;
28
+ float *pVect2 = (float *) pVect2v;
29
+ size_t qty = *((size_t *) qty_ptr);
30
+ float PORTABLE_ALIGN64 TmpRes[16];
31
+ size_t qty16 = qty >> 4;
32
+
33
+ const float *pEnd1 = pVect1 + (qty16 << 4);
34
+
35
+ __m512 diff, v1, v2;
36
+ __m512 sum = _mm512_set1_ps(0);
37
+
38
+ while (pVect1 < pEnd1) {
39
+ v1 = _mm512_loadu_ps(pVect1);
40
+ pVect1 += 16;
41
+ v2 = _mm512_loadu_ps(pVect2);
42
+ pVect2 += 16;
43
+ diff = _mm512_sub_ps(v1, v2);
44
+ // sum = _mm512_fmadd_ps(diff, diff, sum);
45
+ sum = _mm512_add_ps(sum, _mm512_mul_ps(diff, diff));
46
+ }
47
+
48
+ _mm512_store_ps(TmpRes, sum);
49
+ float res = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] +
50
+ TmpRes[7] + TmpRes[8] + TmpRes[9] + TmpRes[10] + TmpRes[11] + TmpRes[12] +
51
+ TmpRes[13] + TmpRes[14] + TmpRes[15];
52
+
53
+ return (res);
54
+ }
55
+
56
+ #elif defined(USE_AVX)
23
57
 
24
58
  // Favor using AVX if available.
25
59
  static float
@@ -106,7 +140,7 @@ namespace hnswlib {
106
140
  }
107
141
  #endif
108
142
 
109
- #if defined(USE_SSE) || defined(USE_AVX)
143
+ #if defined(USE_SSE) || defined(USE_AVX) || defined(USE_AVX512)
110
144
  static float
111
145
  L2SqrSIMD16ExtResiduals(const void *pVect1v, const void *pVect2v, const void *qty_ptr) {
112
146
  size_t qty = *((size_t *) qty_ptr);
@@ -175,7 +209,7 @@ namespace hnswlib {
175
209
  L2Space() : data_size_(0), dim_(0) { }
176
210
  L2Space(size_t dim) {
177
211
  fstdistfunc_ = L2Sqr;
178
- #if defined(USE_SSE) || defined(USE_AVX)
212
+ #if defined(USE_SSE) || defined(USE_AVX) || defined(USE_AVX512)
179
213
  if (dim % 16 == 0)
180
214
  fstdistfunc_ = L2SqrSIMD16Ext;
181
215
  else if (dim % 4 == 0)
@@ -279,4 +313,4 @@ namespace hnswlib {
279
313
  };
280
314
 
281
315
 
282
- }
316
+ }
@@ -2,6 +2,7 @@
2
2
 
3
3
  #include <mutex>
4
4
  #include <string.h>
5
+ #include <deque>
5
6
 
6
7
  namespace hnswlib {
7
8
  typedef unsigned short int vl_type;
@@ -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.4.0'
6
+ VERSION = '0.5.0'
7
7
 
8
8
  # The version of Hnswlib included with gem.
9
- HSWLIB_VERSION = '0.5.2'
9
+ HSWLIB_VERSION = '0.6.0'
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.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - yoshoku
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-11 00:00:00.000000000 Z
11
+ date: 2021-12-11 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Hnswlib.rb provides Ruby bindings for the Hnswlib.
14
14
  email:
@@ -64,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
64
64
  - !ruby/object:Gem::Version
65
65
  version: '0'
66
66
  requirements: []
67
- rubygems_version: 3.2.22
67
+ rubygems_version: 3.2.32
68
68
  signing_key:
69
69
  specification_version: 4
70
70
  summary: Ruby bindings for the Hnswlib.