Skip to content

Commit 7decd52

Browse files
authored
Allow incubating Panama Vector in simdvec, and add vectorized ipByteBin (#112933)
Add support for vectorized ipByteBin. The structure of the implementation and loading framework mirror that of Lucene, but is simplified by avoiding reflective loading since ES has support for a MRJar section for 21. For now, we just disable warnings-as-errors in this small sourceset, since -Xlint:-incubating is only support since JDK 22. The number of source files is small here. Will investigate how to assert that just the single incubating warning is emitted by javac, at a later point.
1 parent e1bba9b commit 7decd52

File tree

13 files changed

+586
-0
lines changed

13 files changed

+586
-0
lines changed

docs/changelog/112933.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 112933
2+
summary: "Allow incubating Panama Vector in simdvec, and add vectorized `ipByteBin`"
3+
area: Search
4+
type: enhancement
5+
issues: []

libs/simdvec/build.gradle

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@ dependencies {
2323
}
2424
}
2525

26+
tasks.named("compileMain21Java").configure {
27+
options.compilerArgs << '--add-modules=jdk.incubator.vector'
28+
// we remove Werror, since incubating suppression (-Xlint:-incubating)
29+
// is only support since JDK 22
30+
options.compilerArgs -= '-Werror'
31+
}
32+
33+
test {
34+
if (JavaVersion.current().majorVersion.toInteger() >= 21) {
35+
jvmArgs '--add-modules=jdk.incubator.vector'
36+
}
37+
}
38+
2639
tasks.withType(CheckForbiddenApisTask).configureEach {
2740
replaceSignatureFiles 'jdk-signatures'
2841
}

libs/simdvec/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
module org.elasticsearch.simdvec {
1111
requires org.elasticsearch.nativeaccess;
1212
requires org.apache.lucene.core;
13+
requires org.elasticsearch.logging;
1314

1415
exports org.elasticsearch.simdvec to org.elasticsearch.server;
1516
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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.simdvec;
11+
12+
import org.elasticsearch.simdvec.internal.vectorization.ESVectorUtilSupport;
13+
import org.elasticsearch.simdvec.internal.vectorization.ESVectorizationProvider;
14+
15+
import static org.elasticsearch.simdvec.internal.vectorization.ESVectorUtilSupport.B_QUERY;
16+
17+
public class ESVectorUtil {
18+
19+
private static final ESVectorUtilSupport IMPL = ESVectorizationProvider.getInstance().getVectorUtilSupport();
20+
21+
public static long ipByteBinByte(byte[] q, byte[] d) {
22+
if (q.length != d.length * B_QUERY) {
23+
throw new IllegalArgumentException("vector dimensions incompatible: " + q.length + "!= " + B_QUERY + " x " + d.length);
24+
}
25+
return IMPL.ipByteBinByte(q, d);
26+
}
27+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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.simdvec.internal.vectorization;
11+
12+
import org.apache.lucene.util.BitUtil;
13+
14+
final class DefaultESVectorUtilSupport implements ESVectorUtilSupport {
15+
16+
DefaultESVectorUtilSupport() {}
17+
18+
@Override
19+
public long ipByteBinByte(byte[] q, byte[] d) {
20+
return ipByteBinByteImpl(q, d);
21+
}
22+
23+
public static long ipByteBinByteImpl(byte[] q, byte[] d) {
24+
long ret = 0;
25+
int size = d.length;
26+
for (int i = 0; i < B_QUERY; i++) {
27+
int r = 0;
28+
long subRet = 0;
29+
for (final int upperBound = d.length & -Integer.BYTES; r < upperBound; r += Integer.BYTES) {
30+
subRet += Integer.bitCount((int) BitUtil.VH_NATIVE_INT.get(q, i * size + r) & (int) BitUtil.VH_NATIVE_INT.get(d, r));
31+
}
32+
for (; r < d.length; r++) {
33+
subRet += Integer.bitCount((q[i * size + r] & d[r]) & 0xFF);
34+
}
35+
ret += subRet << i;
36+
}
37+
return ret;
38+
}
39+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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.simdvec.internal.vectorization;
11+
12+
final class DefaultESVectorizationProvider extends ESVectorizationProvider {
13+
private final ESVectorUtilSupport vectorUtilSupport;
14+
15+
DefaultESVectorizationProvider() {
16+
vectorUtilSupport = new DefaultESVectorUtilSupport();
17+
}
18+
19+
@Override
20+
public ESVectorUtilSupport getVectorUtilSupport() {
21+
return vectorUtilSupport;
22+
}
23+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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.simdvec.internal.vectorization;
11+
12+
public interface ESVectorUtilSupport {
13+
14+
short B_QUERY = 4;
15+
16+
long ipByteBinByte(byte[] q, byte[] d);
17+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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.simdvec.internal.vectorization;
11+
12+
import java.util.Objects;
13+
14+
public abstract class ESVectorizationProvider {
15+
16+
public static ESVectorizationProvider getInstance() {
17+
return Objects.requireNonNull(
18+
ESVectorizationProvider.Holder.INSTANCE,
19+
"call to getInstance() from subclass of VectorizationProvider"
20+
);
21+
}
22+
23+
ESVectorizationProvider() {}
24+
25+
public abstract ESVectorUtilSupport getVectorUtilSupport();
26+
27+
// visible for tests
28+
static ESVectorizationProvider lookup(boolean testMode) {
29+
return new DefaultESVectorizationProvider();
30+
}
31+
32+
/** This static holder class prevents classloading deadlock. */
33+
private static final class Holder {
34+
private Holder() {}
35+
36+
static final ESVectorizationProvider INSTANCE = lookup(false);
37+
}
38+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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.simdvec.internal.vectorization;
11+
12+
import org.apache.lucene.util.Constants;
13+
import org.elasticsearch.logging.LogManager;
14+
import org.elasticsearch.logging.Logger;
15+
16+
import java.util.Locale;
17+
import java.util.Objects;
18+
import java.util.Optional;
19+
20+
public abstract class ESVectorizationProvider {
21+
22+
protected static final Logger logger = LogManager.getLogger(ESVectorizationProvider.class);
23+
24+
public static ESVectorizationProvider getInstance() {
25+
return Objects.requireNonNull(
26+
ESVectorizationProvider.Holder.INSTANCE,
27+
"call to getInstance() from subclass of VectorizationProvider"
28+
);
29+
}
30+
31+
ESVectorizationProvider() {}
32+
33+
public abstract ESVectorUtilSupport getVectorUtilSupport();
34+
35+
// visible for tests
36+
static ESVectorizationProvider lookup(boolean testMode) {
37+
final int runtimeVersion = Runtime.version().feature();
38+
assert runtimeVersion >= 21;
39+
if (runtimeVersion <= 23) {
40+
// only use vector module with Hotspot VM
41+
if (Constants.IS_HOTSPOT_VM == false) {
42+
logger.warn("Java runtime is not using Hotspot VM; Java vector incubator API can't be enabled.");
43+
return new DefaultESVectorizationProvider();
44+
}
45+
// is the incubator module present and readable (JVM providers may to exclude them or it is
46+
// build with jlink)
47+
final var vectorMod = lookupVectorModule();
48+
if (vectorMod.isEmpty()) {
49+
logger.warn(
50+
"Java vector incubator module is not readable. "
51+
+ "For optimal vector performance, pass '--add-modules jdk.incubator.vector' to enable Vector API."
52+
);
53+
return new DefaultESVectorizationProvider();
54+
}
55+
vectorMod.ifPresent(ESVectorizationProvider.class.getModule()::addReads);
56+
var impl = new PanamaESVectorizationProvider();
57+
logger.info(
58+
String.format(
59+
Locale.ENGLISH,
60+
"Java vector incubator API enabled; uses preferredBitSize=%d",
61+
PanamaESVectorUtilSupport.VECTOR_BITSIZE
62+
)
63+
);
64+
return impl;
65+
} else {
66+
logger.warn(
67+
"You are running with unsupported Java "
68+
+ runtimeVersion
69+
+ ". To make full use of the Vector API, please update Elasticsearch."
70+
);
71+
}
72+
return new DefaultESVectorizationProvider();
73+
}
74+
75+
private static Optional<Module> lookupVectorModule() {
76+
return Optional.ofNullable(ESVectorizationProvider.class.getModule().getLayer())
77+
.orElse(ModuleLayer.boot())
78+
.findModule("jdk.incubator.vector");
79+
}
80+
81+
/** This static holder class prevents classloading deadlock. */
82+
private static final class Holder {
83+
private Holder() {}
84+
85+
static final ESVectorizationProvider INSTANCE = lookup(false);
86+
}
87+
}

0 commit comments

Comments
 (0)