Skip to content

Commit b16e36c

Browse files
author
Igor Motov
committed
Merge CAGRA indexes on the GPU instead of rebuilding from vectors
mergeOneField now hands the segments' CAGRA indexes to the cuVS merge API, falling back to the vector based merge when the merged rows cannot be lined up with the merged flat vectors. Deleted documents are dropped through a row, which needs a new CagraIndex.merge overload taking a BitSet. Also cleans up some merge overloads on CuVSProvider and CagraIndex. Closes #1078
1 parent fdd404f commit b16e36c

10 files changed

Lines changed: 1063 additions & 161 deletions

File tree

java/cuvs-java/src/main/java/com/nvidia/cuvs/CagraIndex.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.io.InputStream;
99
import java.io.OutputStream;
1010
import java.nio.file.Path;
11+
import java.util.BitSet;
1112
import java.util.Objects;
1213

1314
/**
@@ -192,6 +193,13 @@ public StandardDataset() {}
192193
*/
193194
long getGraphDegree();
194195

196+
/**
197+
* Returns the number of vectors in this index.
198+
*
199+
* @return the number of rows of the indexed dataset
200+
*/
201+
long size();
202+
195203
/**
196204
* A method to persist a CAGRA index using an instance of {@link OutputStream}
197205
* for writing index bytes.
@@ -310,7 +318,7 @@ static Builder newBuilder(CuVSResources cuvsResources) {
310318
* @throws Throwable if an error occurs during the merge operation
311319
*/
312320
static CagraIndex merge(CagraIndex[] indexes) throws Throwable {
313-
return merge(indexes, null);
321+
return merge(indexes, null, null);
314322
}
315323

316324
/**
@@ -322,6 +330,28 @@ static CagraIndex merge(CagraIndex[] indexes) throws Throwable {
322330
* @throws Throwable if an error occurs during the merge operation
323331
*/
324332
static CagraIndex merge(CagraIndex[] indexes, CagraIndexParams mergeParams) throws Throwable {
333+
return merge(indexes, mergeParams, null);
334+
}
335+
336+
/**
337+
* Merges multiple CAGRA indexes into a single index, keeping only the rows selected by
338+
* {@code rowFilter}.
339+
*
340+
* <p>The merge concatenates the input datasets in the order the indexes are given, so bit
341+
* {@code i} of the filter refers to row {@code i} of that concatenation: bits {@code 0} to
342+
* {@code indexes[0].size() - 1} address the first index, the bits that follow address the second,
343+
* and so on. A <b>set</b> bit keeps the row; a clear bit drops it. The rows that survive keep
344+
* their relative order and are packed together, so the merged index has one row per set bit.
345+
*
346+
* @param indexes Array of CAGRA indexes to merge
347+
* @param mergeParams Parameters to control the merge operation, or null to use defaults
348+
* @param rowFilter The rows to keep, or null to keep all of them
349+
* @return A new merged CAGRA index
350+
* @throws IllegalArgumentException if {@code rowFilter} has a bit set beyond the last row
351+
* @throws Throwable if an error occurs during the merge operation
352+
*/
353+
static CagraIndex merge(CagraIndex[] indexes, CagraIndexParams mergeParams, BitSet rowFilter)
354+
throws Throwable {
325355
if (indexes == null || indexes.length == 0) {
326356
throw new IllegalArgumentException("At least one index must be provided for merging");
327357
}
@@ -333,7 +363,7 @@ static CagraIndex merge(CagraIndex[] indexes, CagraIndexParams mergeParams) thro
333363
}
334364
}
335365

336-
return CuVSProvider.provider().mergeCagraIndexes(indexes, mergeParams);
366+
return CuVSProvider.provider().mergeCagraIndexes(indexes, mergeParams, rowFilter);
337367
}
338368

339369
/**

java/cuvs-java/src/main/java/com/nvidia/cuvs/spi/CuVSProvider.java

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.lang.invoke.MethodType;
1010
import java.nio.file.Path;
1111
import java.time.Duration;
12+
import java.util.BitSet;
1213
import java.util.List;
1314

1415
/**
@@ -165,29 +166,6 @@ HnswIndex hnswIndexBuild(CuVSResources resources, HnswIndexParams hnswParams, Cu
165166
TieredIndex.Builder newTieredIndexBuilder(CuVSResources cuVSResources)
166167
throws UnsupportedOperationException;
167168

168-
/**
169-
* Merges multiple CAGRA indexes into a single index.
170-
*
171-
* @param indexes Array of CAGRA indexes to merge
172-
* @return A new merged CAGRA index
173-
* @throws Throwable if an error occurs during the merge operation
174-
*/
175-
CagraIndex mergeCagraIndexes(CagraIndex[] indexes) throws Throwable;
176-
177-
/**
178-
* Merges multiple CAGRA indexes into a single index with the specified merge parameters.
179-
*
180-
* @param indexes Array of CAGRA indexes to merge
181-
* @param mergeParams Parameters to control the merge operation, or null to use defaults
182-
* @return A new merged CAGRA index
183-
* @throws Throwable if an error occurs during the merge operation
184-
*/
185-
default CagraIndex mergeCagraIndexes(CagraIndex[] indexes, CagraIndexParams mergeParams)
186-
throws Throwable {
187-
// Default implementation falls back to the method without parameters
188-
return mergeCagraIndexes(indexes);
189-
}
190-
191169
/**
192170
* Reports whether the rows of {@code dataset} already sit at the row stride CAGRA requires, which
193171
* is the row length in bytes rounded up to a 16 byte boundary.
@@ -207,6 +185,20 @@ default boolean isCagraPaddedDataset(CuVSMatrix dataset) {
207185
"Padded layout detection is not supported by " + getClass().getName());
208186
}
209187

188+
/**
189+
* Merges multiple CAGRA indexes into a single index, keeping only the rows selected by
190+
* {@code rowFilter}. See {@link CagraIndex#merge(CagraIndex[], CagraIndexParams, BitSet)} for the
191+
* meaning of the filter.
192+
*
193+
* @param indexes Array of CAGRA indexes to merge
194+
* @param mergeParams Parameters to control the merge operation, or null to use defaults
195+
* @param rowFilter The rows to keep, or null to keep all of them
196+
* @return A new merged CAGRA index
197+
* @throws Throwable if an error occurs during the merge operation
198+
*/
199+
CagraIndex mergeCagraIndexes(CagraIndex[] indexes, CagraIndexParams mergeParams, BitSet rowFilter)
200+
throws Throwable;
201+
210202
/**
211203
* Creates a device-backed multi-partition filter handle from the pre-packed combined bitset.
212204
* Per-partition bit offsets are recomputed inside cuVS from the index sizes.

java/cuvs-java/src/main/java/com/nvidia/cuvs/spi/UnsupportedProvider.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.lang.invoke.MethodHandle;
99
import java.nio.file.Path;
1010
import java.time.Duration;
11+
import java.util.BitSet;
1112
import java.util.List;
1213
import java.util.logging.Level;
1314

@@ -81,12 +82,13 @@ public TieredIndex.Builder newTieredIndexBuilder(CuVSResources cuVSResources) {
8182
}
8283

8384
@Override
84-
public CagraIndex mergeCagraIndexes(CagraIndex[] indexes) {
85+
public boolean isCagraPaddedDataset(CuVSMatrix dataset) {
8586
throw new UnsupportedOperationException(reasons);
8687
}
8788

8889
@Override
89-
public boolean isCagraPaddedDataset(CuVSMatrix dataset) {
90+
public CagraIndex mergeCagraIndexes(
91+
CagraIndex[] indexes, CagraIndexParams mergeParams, BitSet rowFilter) {
9092
throw new UnsupportedOperationException(reasons);
9193
}
9294

0 commit comments

Comments
 (0)