hnswlib 0.4.0 → 0.5.0

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: 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.