@@ -580,13 +580,12 @@ HnswLoadElement(HnswElement element, float *distance, Datum *q, Relation index,
580
580
}
581
581
582
582
/*
583
- * Get the distance for a candidate
583
+ * Get the distance for an element
584
584
*/
585
585
static float
586
- GetCandidateDistance (char * base , HnswCandidate * hc , Datum q , FmgrInfo * procinfo , Oid collation )
586
+ GetElementDistance (char * base , HnswElement element , Datum q , FmgrInfo * procinfo , Oid collation )
587
587
{
588
- HnswElement hce = HnswPtrAccess (base , hc -> element );
589
- Datum value = HnswGetValue (base , hce );
588
+ Datum value = HnswGetValue (base , element );
590
589
591
590
return DatumGetFloat8 (FunctionCall2Coll (procinfo , collation , q , value ));
592
591
}
@@ -601,7 +600,7 @@ HnswEntryCandidate(char *base, HnswElement entryPoint, Datum q, Relation index,
601
600
602
601
HnswPtrStore (base , hc -> element , entryPoint );
603
602
if (index == NULL )
604
- hc -> distance = GetCandidateDistance (base , hc , q , procinfo , collation );
603
+ hc -> distance = GetElementDistance (base , entryPoint , q , procinfo , collation );
605
604
else
606
605
HnswLoadElement (entryPoint , & hc -> distance , & q , index , procinfo , collation , loadVec , NULL );
607
606
return hc ;
@@ -706,20 +705,87 @@ AddToVisited(char *base, visited_hash * v, HnswCandidate * hc, Relation index, b
706
705
* Count element towards ef
707
706
*/
708
707
static inline bool
709
- CountElement (char * base , HnswElement skipElement , HnswCandidate * hc )
708
+ CountElement (char * base , HnswElement skipElement , HnswElement e )
710
709
{
711
- HnswElement e ;
712
-
713
710
if (skipElement == NULL )
714
711
return true;
715
712
716
713
/* Ensure does not access heaptidsLength during in-memory build */
717
714
pg_memory_barrier ();
718
715
719
- e = HnswPtrAccess (base , hc -> element );
720
716
return e -> heaptidsLength != 0 ;
721
717
}
722
718
719
+ /*
720
+ * Load unvisited neighbors from memory
721
+ */
722
+ static void
723
+ HnswLoadUnvisitedFromMemory (char * base , HnswElement element , HnswElement * unvisited , int * unvisitedLength , visited_hash * v , int lc , HnswNeighborArray * neighborhoodData , Size neighborhoodSize )
724
+ {
725
+ /* Get the neighborhood at layer lc */
726
+ HnswNeighborArray * neighborhood = HnswGetNeighbors (base , element , lc );
727
+
728
+ /* Copy neighborhood to local memory */
729
+ LWLockAcquire (& element -> lock , LW_SHARED );
730
+ memcpy (neighborhoodData , neighborhood , neighborhoodSize );
731
+ LWLockRelease (& element -> lock );
732
+ neighborhood = neighborhoodData ;
733
+
734
+ * unvisitedLength = 0 ;
735
+
736
+ for (int i = 0 ; i < neighborhood -> length ; i ++ )
737
+ {
738
+ HnswCandidate * hc = & neighborhood -> items [i ];
739
+ bool found ;
740
+
741
+ AddToVisited (base , v , hc , NULL , & found );
742
+
743
+ if (!found )
744
+ unvisited [(* unvisitedLength )++ ] = HnswPtrAccess (base , hc -> element );
745
+ }
746
+ }
747
+
748
+ /*
749
+ * Load unvisited neighbors from disk
750
+ */
751
+ static void
752
+ HnswLoadUnvisitedFromDisk (HnswElement element , HnswElement * unvisited , int * unvisitedLength , visited_hash * v , Relation index , int m , int lm , int lc )
753
+ {
754
+ Buffer buf ;
755
+ Page page ;
756
+ HnswNeighborTuple ntup ;
757
+ int start ;
758
+ ItemPointerData indextids [HNSW_MAX_M * 2 ];
759
+
760
+ buf = ReadBuffer (index , element -> neighborPage );
761
+ LockBuffer (buf , BUFFER_LOCK_SHARE );
762
+ page = BufferGetPage (buf );
763
+
764
+ ntup = (HnswNeighborTuple ) PageGetItem (page , PageGetItemId (page , element -> neighborOffno ));
765
+ start = (element -> level - lc ) * m ;
766
+
767
+ /* Copy to minimize lock time */
768
+ memcpy (& indextids , ntup -> indextids + start , lm * sizeof (ItemPointerData ));
769
+
770
+ UnlockReleaseBuffer (buf );
771
+
772
+ * unvisitedLength = 0 ;
773
+
774
+ for (int i = 0 ; i < lm ; i ++ )
775
+ {
776
+ ItemPointer indextid = & indextids [i ];
777
+ bool found ;
778
+
779
+ if (!ItemPointerIsValid (indextid ))
780
+ break ;
781
+
782
+ tidhash_insert (v -> tids , * indextid , & found );
783
+
784
+ if (!found )
785
+ unvisited [(* unvisitedLength )++ ] = HnswInitElementFromBlock (ItemPointerGetBlockNumber (indextid ), ItemPointerGetOffsetNumber (indextid ));
786
+ }
787
+ }
788
+
723
789
/*
724
790
* Algorithm 2 from paper
725
791
*/
@@ -734,13 +800,16 @@ HnswSearchLayer(char *base, Datum q, List *ep, int ef, int lc, Relation index, F
734
800
ListCell * lc2 ;
735
801
HnswNeighborArray * neighborhoodData = NULL ;
736
802
Size neighborhoodSize = 0 ;
803
+ int lm = HnswGetLayerM (m , lc );
804
+ HnswElement * unvisited = palloc (lm * sizeof (HnswElement ));
805
+ int unvisitedLength ;
737
806
738
807
InitVisited (base , & v , index , ef , m );
739
808
740
809
/* Create local memory for neighborhood if needed */
741
810
if (index == NULL )
742
811
{
743
- neighborhoodSize = HNSW_NEIGHBOR_ARRAY_SIZE (HnswGetLayerM ( m , lc ) );
812
+ neighborhoodSize = HNSW_NEIGHBOR_ARRAY_SIZE (lm );
744
813
neighborhoodData = palloc (neighborhoodSize );
745
814
}
746
815
@@ -762,13 +831,12 @@ HnswSearchLayer(char *base, Datum q, List *ep, int ef, int lc, Relation index, F
762
831
* would be ideal to do this for inserts as well, but this could
763
832
* affect insert performance.
764
833
*/
765
- if (CountElement (base , skipElement , hc ))
834
+ if (CountElement (base , skipElement , HnswPtrAccess ( base , hc -> element ) ))
766
835
wlen ++ ;
767
836
}
768
837
769
838
while (!pairingheap_is_empty (C ))
770
839
{
771
- HnswNeighborArray * neighborhood ;
772
840
HnswCandidate * c = HnswGetPairingHeapCandidate (c_node , pairingheap_remove_first (C ));
773
841
HnswCandidate * f = HnswGetPairingHeapCandidate (w_node , pairingheap_first (W ));
774
842
HnswElement cElement ;
@@ -778,74 +846,56 @@ HnswSearchLayer(char *base, Datum q, List *ep, int ef, int lc, Relation index, F
778
846
779
847
cElement = HnswPtrAccess (base , c -> element );
780
848
781
- if (HnswPtrIsNull (base , cElement -> neighbors ))
782
- HnswLoadNeighbors (cElement , index , m );
783
-
784
- /* Get the neighborhood at layer lc */
785
- neighborhood = HnswGetNeighbors (base , cElement , lc );
786
-
787
- /* Copy neighborhood to local memory if needed */
788
849
if (index == NULL )
789
- {
790
- LWLockAcquire (& cElement -> lock , LW_SHARED );
791
- memcpy (neighborhoodData , neighborhood , neighborhoodSize );
792
- LWLockRelease (& cElement -> lock );
793
- neighborhood = neighborhoodData ;
794
- }
850
+ HnswLoadUnvisitedFromMemory (base , cElement , unvisited , & unvisitedLength , & v , lc , neighborhoodData , neighborhoodSize );
851
+ else
852
+ HnswLoadUnvisitedFromDisk (cElement , unvisited , & unvisitedLength , & v , index , m , lm , lc );
795
853
796
- for (int i = 0 ; i < neighborhood -> length ; i ++ )
854
+ for (int i = 0 ; i < unvisitedLength ; i ++ )
797
855
{
798
- HnswCandidate * e = & neighborhood -> items [i ];
799
- bool visited ;
856
+ HnswElement eElement = unvisited [i ];
857
+ float eDistance ;
858
+ bool alwaysAdd = wlen < ef ;
859
+
860
+ f = HnswGetPairingHeapCandidate (w_node , pairingheap_first (W ));
800
861
801
- AddToVisited (base , & v , e , index , & visited );
862
+ if (index == NULL )
863
+ eDistance = GetElementDistance (base , eElement , q , procinfo , collation );
864
+ else
865
+ HnswLoadElement (eElement , & eDistance , & q , index , procinfo , collation , inserting , alwaysAdd ? NULL : & f -> distance );
802
866
803
- if (! visited )
867
+ if (eDistance < f -> distance || alwaysAdd )
804
868
{
805
- float eDistance ;
806
- HnswElement eElement = HnswPtrAccess (base , e -> element );
807
- bool alwaysAdd = wlen < ef ;
869
+ HnswCandidate * e ;
870
+ HnswPairingHeapNode * node ;
808
871
809
- f = HnswGetPairingHeapCandidate ( w_node , pairingheap_first ( W ) );
872
+ Assert (! eElement -> deleted );
810
873
811
- if (index == NULL )
812
- eDistance = GetCandidateDistance (base , e , q , procinfo , collation );
813
- else
814
- HnswLoadElement (eElement , & eDistance , & q , index , procinfo , collation , inserting , alwaysAdd ? NULL : & f -> distance );
874
+ /* Make robust to issues */
875
+ if (eElement -> level < lc )
876
+ continue ;
877
+
878
+ /* Create a new candidate */
879
+ e = palloc (sizeof (HnswCandidate ));
880
+ HnswPtrStore (base , e -> element , eElement );
881
+ e -> distance = eDistance ;
882
+
883
+ node = CreatePairingHeapNode (e );
884
+ pairingheap_add (C , & node -> c_node );
885
+ pairingheap_add (W , & node -> w_node );
815
886
816
- if (eDistance < f -> distance || alwaysAdd )
887
+ /*
888
+ * Do not count elements being deleted towards ef when
889
+ * vacuuming. It would be ideal to do this for inserts as
890
+ * well, but this could affect insert performance.
891
+ */
892
+ if (CountElement (base , skipElement , eElement ))
817
893
{
818
- HnswCandidate * ec ;
819
- HnswPairingHeapNode * node ;
820
-
821
- Assert (!eElement -> deleted );
822
-
823
- /* Make robust to issues */
824
- if (eElement -> level < lc )
825
- continue ;
826
-
827
- /* Copy e */
828
- ec = palloc (sizeof (HnswCandidate ));
829
- HnswPtrStore (base , ec -> element , eElement );
830
- ec -> distance = eDistance ;
831
-
832
- node = CreatePairingHeapNode (ec );
833
- pairingheap_add (C , & node -> c_node );
834
- pairingheap_add (W , & node -> w_node );
835
-
836
- /*
837
- * Do not count elements being deleted towards ef when
838
- * vacuuming. It would be ideal to do this for inserts as
839
- * well, but this could affect insert performance.
840
- */
841
- if (CountElement (base , skipElement , e ))
842
- {
843
- wlen ++ ;
844
-
845
- /* No need to decrement wlen */
846
- if (wlen > ef )
847
- pairingheap_remove_first (W );
848
- }
894
+ wlen ++ ;
895
+
896
+ /* No need to decrement wlen */
897
+ if (wlen > ef )
898
+ pairingheap_remove_first (W );
849
899
}
850
900
}
851
901
}
@@ -1117,7 +1167,7 @@ HnswUpdateConnection(char *base, HnswElement element, HnswCandidate * hc, int lm
1117
1167
if (HnswPtrIsNull (base , hc3Element -> value ))
1118
1168
HnswLoadElement (hc3Element , & hc3 -> distance , & q , index , procinfo , collation , true, NULL );
1119
1169
else
1120
- hc3 -> distance = GetCandidateDistance (base , hc3 , q , procinfo , collation );
1170
+ hc3 -> distance = GetElementDistance (base , hc3Element , q , procinfo , collation );
1121
1171
1122
1172
/* Prune element if being deleted */
1123
1173
if (hc3Element -> heaptidsLength == 0 )
0 commit comments