Skip to content

Commit 79dc393

Browse files
authored
[GPU] Optimize merge memory usage (#136411) (#136825)
This PR changes how we gather and compact vector data for transmitting them to the GPU. Instead of using a temporary file to write out the compacted arrays, we use directly the vector values from the scorer supplier, which are backed by a memory mapped input. This way we avoid an additional copy of the data.
1 parent 4ee4562 commit 79dc393

File tree

6 files changed

+288
-115
lines changed

6 files changed

+288
-115
lines changed

distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemJvmOptions.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ static List<String> systemJvmOptions(Settings nodeSettings, final Map<String, St
6060
"-Dio.netty.noUnsafe=true",
6161
"-Dio.netty.noKeySetOptimization=true",
6262
"-Dio.netty.recycler.maxCapacityPerThread=0",
63+
// Needed to get access to raw vectors from Lucene scorers
64+
"--add-opens=org.apache.lucene.core/org.apache.lucene.codecs.lucene99=org.elasticsearch.server",
65+
"--add-opens=org.apache.lucene.core/org.apache.lucene.internal.vectorization=org.elasticsearch.server",
6366
// log4j 2
6467
"-Dlog4j.shutdownHookEnabled=false",
6568
"-Dlog4j2.disable.jmx=true",

server/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,7 @@
488488
exports org.elasticsearch.lucene.util.automaton;
489489
exports org.elasticsearch.index.codec.perfield;
490490
exports org.elasticsearch.index.codec.vectors to org.elasticsearch.test.knn, org.elasticsearch.gpu;
491+
exports org.elasticsearch.index.codec.vectors.reflect to org.elasticsearch.gpu;
491492
exports org.elasticsearch.index.codec.vectors.es818 to org.elasticsearch.test.knn;
492493
exports org.elasticsearch.inference.telemetry;
493494
exports org.elasticsearch.index.codec.vectors.diskbbq to org.elasticsearch.test.knn;
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.index.codec.vectors.reflect;
11+
12+
import org.apache.lucene.codecs.lucene95.HasIndexSlice;
13+
import org.apache.lucene.codecs.lucene99.Lucene99ScalarQuantizedVectorsWriter;
14+
import org.apache.lucene.index.FloatVectorValues;
15+
import org.apache.lucene.util.hnsw.CloseableRandomVectorScorerSupplier;
16+
import org.apache.lucene.util.hnsw.RandomVectorScorerSupplier;
17+
import org.elasticsearch.simdvec.QuantizedByteVectorValuesAccess;
18+
19+
import java.lang.invoke.MethodHandles;
20+
import java.lang.invoke.VarHandle;
21+
22+
public class VectorsFormatReflectionUtils {
23+
private static final VarHandle FLOAT_SUPPLIER_HANDLE;
24+
private static final VarHandle BYTE_SUPPLIER_HANDLE;
25+
private static final VarHandle FLOAT_VECTORS_HANDLE;
26+
27+
private static final Class<?> FLAT_CLOSEABLE_RANDOM_VECTOR_SCORER_SUPPLIER_CLASS;
28+
private static final Class<?> SCALAR_QUANTIZED_CLOSEABLE_RANDOM_VECTOR_SCORER_SUPPLIER_CLASS;
29+
private static final Class<?> FLOAT_SCORING_SUPPLIER_CLASS;
30+
static {
31+
try {
32+
FLAT_CLOSEABLE_RANDOM_VECTOR_SCORER_SUPPLIER_CLASS = Class.forName(
33+
"org.apache.lucene.codecs.lucene99.Lucene99FlatVectorsWriter$FlatCloseableRandomVectorScorerSupplier"
34+
);
35+
var lookup = MethodHandles.privateLookupIn(FLAT_CLOSEABLE_RANDOM_VECTOR_SCORER_SUPPLIER_CLASS, MethodHandles.lookup());
36+
FLOAT_SUPPLIER_HANDLE = lookup.findVarHandle(
37+
FLAT_CLOSEABLE_RANDOM_VECTOR_SCORER_SUPPLIER_CLASS,
38+
"supplier",
39+
RandomVectorScorerSupplier.class
40+
);
41+
42+
FLOAT_SCORING_SUPPLIER_CLASS = Class.forName(
43+
"org.apache.lucene.internal.vectorization.Lucene99MemorySegmentFloatVectorScorerSupplier"
44+
);
45+
lookup = MethodHandles.privateLookupIn(FLOAT_SCORING_SUPPLIER_CLASS, MethodHandles.lookup());
46+
FLOAT_VECTORS_HANDLE = lookup.findVarHandle(FLOAT_SCORING_SUPPLIER_CLASS, "values", FloatVectorValues.class);
47+
48+
SCALAR_QUANTIZED_CLOSEABLE_RANDOM_VECTOR_SCORER_SUPPLIER_CLASS = Class.forName(
49+
Lucene99ScalarQuantizedVectorsWriter.class.getCanonicalName() + "$ScalarQuantizedCloseableRandomVectorScorerSupplier"
50+
);
51+
lookup = MethodHandles.privateLookupIn(SCALAR_QUANTIZED_CLOSEABLE_RANDOM_VECTOR_SCORER_SUPPLIER_CLASS, MethodHandles.lookup());
52+
BYTE_SUPPLIER_HANDLE = lookup.findVarHandle(
53+
SCALAR_QUANTIZED_CLOSEABLE_RANDOM_VECTOR_SCORER_SUPPLIER_CLASS,
54+
"supplier",
55+
RandomVectorScorerSupplier.class
56+
);
57+
58+
} catch (IllegalAccessException e) {
59+
throw new AssertionError("should not happen, check opens", e);
60+
} catch (ReflectiveOperationException e) {
61+
throw new AssertionError(e);
62+
}
63+
}
64+
65+
public static RandomVectorScorerSupplier getFlatRandomVectorScorerInnerSupplier(CloseableRandomVectorScorerSupplier scorerSupplier) {
66+
if (scorerSupplier.getClass().equals(FLAT_CLOSEABLE_RANDOM_VECTOR_SCORER_SUPPLIER_CLASS)) {
67+
return (RandomVectorScorerSupplier) FLOAT_SUPPLIER_HANDLE.get(scorerSupplier);
68+
}
69+
return null;
70+
}
71+
72+
public static RandomVectorScorerSupplier getScalarQuantizedRandomVectorScorerInnerSupplier(
73+
CloseableRandomVectorScorerSupplier scorerSupplier
74+
) {
75+
if (scorerSupplier.getClass().equals(SCALAR_QUANTIZED_CLOSEABLE_RANDOM_VECTOR_SCORER_SUPPLIER_CLASS)) {
76+
return (RandomVectorScorerSupplier) BYTE_SUPPLIER_HANDLE.get(scorerSupplier);
77+
}
78+
return null;
79+
}
80+
81+
public static HasIndexSlice getFloatScoringSupplierVectorOrNull(RandomVectorScorerSupplier scorerSupplier) {
82+
if (FLOAT_SCORING_SUPPLIER_CLASS.isAssignableFrom(scorerSupplier.getClass())) {
83+
var vectorValues = FLOAT_VECTORS_HANDLE.get(scorerSupplier);
84+
if (vectorValues instanceof HasIndexSlice indexSlice) {
85+
return indexSlice;
86+
}
87+
}
88+
return null;
89+
}
90+
91+
public static HasIndexSlice getByteScoringSupplierVectorOrNull(RandomVectorScorerSupplier scorerSupplier) {
92+
if (scorerSupplier instanceof QuantizedByteVectorValuesAccess quantizedByteVectorValuesAccess) {
93+
return quantizedByteVectorValuesAccess.get();
94+
}
95+
return null;
96+
}
97+
}

0 commit comments

Comments
 (0)