@@ -66,6 +66,10 @@ const QUANT_STATE_KEY: u32 = u32::from_be_bytes(*b"_qnt");
6666/// Starting capacity of the pre-allocated rerank buffers.
6767const RERANK_BUFFER_LENGTH : usize = 1024 ;
6868
69+ /// Size hint passed to Garnet when batch reading attributes. Attributes are variable
70+ /// length, so this is only an estimate used to size Garnet's read buffer.
71+ const ATTRIBUTE_LENGTH_HINT : usize = 1024 ;
72+
6973#[ derive( Clone ) ]
7074struct AdjList ( AdjacencyList < u32 > ) ;
7175
@@ -424,9 +428,18 @@ impl<T: VectorRepr> GarnetProvider<T> {
424428 id : & GarnetId ,
425429 data : & [ u8 ] ,
426430 ) -> Result < ( ) , GarnetProviderError > {
431+ let mut iid = u32:: MAX ;
432+ if !self . callbacks . read_single_eid (
433+ & context. term ( Term :: IntMap ) ,
434+ id,
435+ bytemuck:: bytes_of_mut ( & mut iid) ,
436+ ) {
437+ return Err ( GarnetError :: Read . into ( ) ) ;
438+ }
439+
427440 if self
428441 . callbacks
429- . write_eid ( & context. term ( Term :: Attributes ) , id , data)
442+ . write_iid ( & context. term ( Term :: Attributes ) , iid , data)
430443 {
431444 Ok ( ( ) )
432445 } else {
@@ -439,9 +452,18 @@ impl<T: VectorRepr> GarnetProvider<T> {
439452 context : & Context ,
440453 id : & GarnetId ,
441454 ) -> Result < ( ) , GarnetProviderError > {
455+ let mut iid = u32:: MAX ;
456+ if !self . callbacks . read_single_eid (
457+ & context. term ( Term :: IntMap ) ,
458+ id,
459+ bytemuck:: bytes_of_mut ( & mut iid) ,
460+ ) {
461+ return Err ( GarnetError :: Read . into ( ) ) ;
462+ }
463+
442464 if self
443465 . callbacks
444- . delete_eid ( & context. term ( Term :: Attributes ) , id )
466+ . delete_iid ( & context. term ( Term :: Attributes ) , iid )
445467 {
446468 Ok ( ( ) )
447469 } else {
@@ -1057,7 +1079,7 @@ impl<T: VectorRepr> Delete for GarnetProvider<T> {
10571079 // It is not an error to fail deleting attributes; they may not exist.
10581080 let _: bool = self
10591081 . callbacks
1060- . delete_eid ( & context. term ( Term :: Attributes ) , gid ) ;
1082+ . delete_iid ( & context. term ( Term :: Attributes ) , id ) ;
10611083
10621084 // TODO: inplace_delete needs access to neighbors. Delete these once that bug is fixed.
10631085 // See https://github.com/microsoft/DiskANN/issues/1153.
@@ -1194,6 +1216,32 @@ impl<'a, T: VectorRepr> DynamicAccessor<'a, T> {
11941216 }
11951217 }
11961218 }
1219+
1220+ /// Batch read the attributes for `filtered_ids` and record the filter result for each
1221+ /// into `filtered_decisions`.
1222+ ///
1223+ /// Garnet skips ids with no stored attributes, so those keep `default_decision`.
1224+ fn compute_filter_decisions ( & mut self , default_decision : bool ) {
1225+ let Self {
1226+ provider,
1227+ context,
1228+ filtered_ids,
1229+ filtered_decisions,
1230+ ..
1231+ } = self ;
1232+
1233+ filtered_decisions. clear ( ) ;
1234+ filtered_decisions. resize ( filtered_ids. len ( ) / 2 , default_decision) ;
1235+
1236+ provider. callbacks . read_multi_lpiid :: < _ , u8 > (
1237+ & context. term ( Term :: Attributes ) ,
1238+ filtered_ids,
1239+ ATTRIBUTE_LENGTH_HINT ,
1240+ |i, attrs| {
1241+ filtered_decisions[ i as usize ] = provider. callbacks . matches_filter ( context, attrs) ;
1242+ } ,
1243+ ) ;
1244+ }
11971245}
11981246
11991247impl < T : VectorRepr > HasId for DynamicAccessor < ' _ , T > {
@@ -1507,12 +1555,13 @@ impl<T: VectorRepr> FilteredAccessor for DynamicAccessor<'_, T> {
15071555 // borrow. We put it back at the end to save the allocation.
15081556 let mut id_buffer = mem:: take ( & mut * * self . id_buffer ) ;
15091557
1558+ let default_decision = self . provider . callbacks . matches_filter ( self . context , & [ ] ) ;
1559+
15101560 for nl_id in ids {
15111561 self . provider
15121562 . get_neighbors ( self . context , nl_id, & mut id_buffer) ;
15131563
15141564 self . filtered_ids . clear ( ) ;
1515- self . filtered_decisions . clear ( ) ;
15161565
15171566 for id in id_buffer. iter ( ) . copied ( ) . filter ( |id| pred. eval_mut ( id) ) {
15181567 if id == Self :: START_ID {
@@ -1522,15 +1571,15 @@ impl<T: VectorRepr> FilteredAccessor for DynamicAccessor<'_, T> {
15221571 } ;
15231572 on_neighbors ( Decision :: reject ( id) , dist) ;
15241573 } else {
1525- let matches = self . provider . callbacks . matches_filter ( self . context , id) ;
1526-
15271574 self . filtered_ids . push ( 4 ) ;
15281575 self . filtered_ids . push ( id) ;
1529-
1530- self . filtered_decisions . push ( matches) ;
15311576 }
15321577 }
15331578
1579+ if self . filtered_ids . is_empty ( ) {
1580+ continue ;
1581+ }
1582+
15341583 let ( ctx, length_hint) = if self . quantized {
15351584 (
15361585 self . context . term ( Term :: Quantized ) ,
@@ -1543,22 +1592,23 @@ impl<T: VectorRepr> FilteredAccessor for DynamicAccessor<'_, T> {
15431592 )
15441593 } ;
15451594
1546- if !self . filtered_ids . is_empty ( ) {
1547- self . provider . callbacks . read_multi_lpiid (
1548- & ctx,
1549- & self . filtered_ids ,
1550- length_hint,
1551- |i, v| {
1552- let dist = self . computer . evaluate_similarity ( v) ;
1553- let decision = if self . filtered_decisions [ i as usize ] {
1554- Decision :: accept ( self . filtered_ids [ i as usize * 2 + 1 ] )
1555- } else {
1556- Decision :: reject ( self . filtered_ids [ i as usize * 2 + 1 ] )
1557- } ;
1558- on_neighbors ( decision, dist) ;
1559- } ,
1560- ) ;
1561- }
1595+ self . compute_filter_decisions ( default_decision) ;
1596+
1597+ // Read vectors and calculate distances
1598+ self . provider . callbacks . read_multi_lpiid (
1599+ & ctx,
1600+ & self . filtered_ids ,
1601+ length_hint,
1602+ |i, v| {
1603+ let dist = self . computer . evaluate_similarity ( v) ;
1604+ let decision = if self . filtered_decisions [ i as usize ] {
1605+ Decision :: accept ( self . filtered_ids [ i as usize * 2 + 1 ] )
1606+ } else {
1607+ Decision :: reject ( self . filtered_ids [ i as usize * 2 + 1 ] )
1608+ } ;
1609+ on_neighbors ( decision, dist) ;
1610+ } ,
1611+ ) ;
15621612 }
15631613
15641614 * * self . id_buffer = id_buffer;
@@ -1580,20 +1630,45 @@ impl<T: VectorRepr> FilteredAccessor for DynamicAccessor<'_, T> {
15801630 // borrow. We put it back at the end to save the allocation.
15811631 let mut id_buffer = mem:: take ( & mut * * self . id_buffer ) ;
15821632
1633+ let default_decision = self . provider . callbacks . matches_filter ( self . context , & [ ] ) ;
1634+
15831635 for nl_id in ids {
15841636 self . provider
15851637 . get_neighbors ( self . context , nl_id, & mut id_buffer) ;
15861638 self . filtered_ids . clear ( ) ;
15871639
15881640 for id in id_buffer. iter ( ) . copied ( ) {
15891641 if id != Self :: START_ID && pred. eval ( & id) {
1590- let matches = self . provider . callbacks . matches_filter ( self . context , id) ;
1642+ self . filtered_ids . push ( 4 ) ;
1643+ self . filtered_ids . push ( id) ;
1644+ }
1645+ }
15911646
1592- if matches && pred. eval_mut ( & Accept :: new ( id) ) {
1593- self . filtered_ids . push ( 4 ) ;
1594- self . filtered_ids . push ( id) ;
1595- }
1647+ if self . filtered_ids . is_empty ( ) {
1648+ continue ;
1649+ }
1650+
1651+ self . compute_filter_decisions ( default_decision) ;
1652+
1653+ // Remove non-matching ids
1654+ let mut index = 0 ;
1655+ for ( i, & matches) in self . filtered_decisions . iter ( ) . enumerate ( ) {
1656+ if !matches {
1657+ continue ;
15961658 }
1659+
1660+ let id = self . filtered_ids [ i * 2 + 1 ] ;
1661+
1662+ if pred. eval_mut ( & Accept :: new ( id) ) {
1663+ self . filtered_ids [ index * 2 ] = 4 ;
1664+ self . filtered_ids [ index * 2 + 1 ] = id;
1665+ index += 1 ;
1666+ }
1667+ }
1668+ self . filtered_ids . truncate ( index * 2 ) ;
1669+
1670+ if self . filtered_ids . is_empty ( ) {
1671+ continue ;
15971672 }
15981673
15991674 let ( ctx, length_hint) = if self . quantized {
@@ -1608,17 +1683,15 @@ impl<T: VectorRepr> FilteredAccessor for DynamicAccessor<'_, T> {
16081683 )
16091684 } ;
16101685
1611- if !self . filtered_ids . is_empty ( ) {
1612- self . provider . callbacks . read_multi_lpiid (
1613- & ctx,
1614- & self . filtered_ids ,
1615- length_hint,
1616- |i, v| {
1617- let dist = self . computer . evaluate_similarity ( v) ;
1618- on_neighbors ( Accept :: new ( self . filtered_ids [ i as usize * 2 + 1 ] ) , dist) ;
1619- } ,
1620- ) ;
1621- }
1686+ self . provider . callbacks . read_multi_lpiid (
1687+ & ctx,
1688+ & self . filtered_ids ,
1689+ length_hint,
1690+ |i, v| {
1691+ let dist = self . computer . evaluate_similarity ( v) ;
1692+ on_neighbors ( Accept :: new ( self . filtered_ids [ i as usize * 2 + 1 ] ) , dist) ;
1693+ } ,
1694+ ) ;
16221695 }
16231696
16241697 * * self . id_buffer = id_buffer;
0 commit comments