mirror of https://github.com/apache/lucene.git
Refactor vectorization support in Lucene (#12410)
This commit is contained in:
parent
907883f701
commit
fde2e50d9e
|
@ -159,6 +159,9 @@ Other
|
|||
* GITHUB#12404: Remove usage and add some legacy java.util classes to forbiddenapis (Stack, Hashtable, Vector).
|
||||
(Uwe Schindler)
|
||||
|
||||
* GITHUB#12410: Refactor vectorization support (split provider from implementation classes).
|
||||
(Uwe Schindler, Chris Hegarty)
|
||||
|
||||
======================== Lucene 9.7.0 =======================
|
||||
|
||||
API Changes
|
||||
|
|
|
@ -15,12 +15,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.util;
|
||||
package org.apache.lucene.internal.vectorization;
|
||||
|
||||
/** The default VectorUtil provider implementation. */
|
||||
final class VectorUtilDefaultProvider implements VectorUtilProvider {
|
||||
final class DefaultVectorUtilSupport implements VectorUtilSupport {
|
||||
|
||||
VectorUtilDefaultProvider() {}
|
||||
DefaultVectorUtilSupport() {}
|
||||
|
||||
@Override
|
||||
public float dotProduct(float[] a, float[] b) {
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.internal.vectorization;
|
||||
|
||||
/** Default provider returning scalar implementations. */
|
||||
final class DefaultVectorizationProvider extends VectorizationProvider {
|
||||
|
||||
private final VectorUtilSupport vectorUtilSupport;
|
||||
|
||||
DefaultVectorizationProvider() {
|
||||
vectorUtilSupport = new DefaultVectorUtilSupport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VectorUtilSupport getVectorUtilSupport() {
|
||||
return vectorUtilSupport;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.internal.vectorization;
|
||||
|
||||
/**
|
||||
* Interface for implementations of VectorUtil support.
|
||||
*
|
||||
* @lucene.internal
|
||||
*/
|
||||
public interface VectorUtilSupport {
|
||||
|
||||
/** Calculates the dot product of the given float arrays. */
|
||||
float dotProduct(float[] a, float[] b);
|
||||
|
||||
/** Returns the cosine similarity between the two vectors. */
|
||||
float cosine(float[] v1, float[] v2);
|
||||
|
||||
/** Returns the sum of squared differences of the two vectors. */
|
||||
float squareDistance(float[] a, float[] b);
|
||||
|
||||
/** Returns the dot product computed over signed bytes. */
|
||||
int dotProduct(byte[] a, byte[] b);
|
||||
|
||||
/** Returns the cosine similarity between the two byte vectors. */
|
||||
float cosine(byte[] a, byte[] b);
|
||||
|
||||
/** Returns the sum of squared differences of the two byte vectors. */
|
||||
int squareDistance(byte[] a, byte[] b);
|
||||
}
|
|
@ -15,9 +15,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.util;
|
||||
package org.apache.lucene.internal.vectorization;
|
||||
|
||||
import java.lang.Runtime.Version;
|
||||
import java.lang.StackWalker.StackFrame;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.security.AccessController;
|
||||
|
@ -25,36 +26,51 @@ import java.security.PrivilegedAction;
|
|||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.lucene.util.SuppressForbidden;
|
||||
import org.apache.lucene.util.VectorUtil;
|
||||
|
||||
/** A provider of VectorUtil implementations. */
|
||||
interface VectorUtilProvider {
|
||||
/**
|
||||
* A provider of vectorization implementations. Depending on the Java version and availability of
|
||||
* vectorization modules in the Java runtime this class provides optimized implementations (using
|
||||
* SIMD) of several algorithms used throughout Apache Lucene.
|
||||
*
|
||||
* @lucene.internal
|
||||
*/
|
||||
public abstract class VectorizationProvider {
|
||||
|
||||
/** Calculates the dot product of the given float arrays. */
|
||||
float dotProduct(float[] a, float[] b);
|
||||
/**
|
||||
* Returns the default instance of the provider matching vectorization possibilities of actual
|
||||
* runtime.
|
||||
*
|
||||
* @throws UnsupportedOperationException if the singleton getter is not called from known Lucene
|
||||
* classes.
|
||||
*/
|
||||
public static VectorizationProvider getInstance() {
|
||||
ensureCaller();
|
||||
return Objects.requireNonNull(
|
||||
Holder.INSTANCE, "call to getInstance() from subclass of VectorizationProvider");
|
||||
}
|
||||
|
||||
/** Returns the cosine similarity between the two vectors. */
|
||||
float cosine(float[] v1, float[] v2);
|
||||
VectorizationProvider() {
|
||||
// no instance/subclass except from this package
|
||||
}
|
||||
|
||||
/** Returns the sum of squared differences of the two vectors. */
|
||||
float squareDistance(float[] a, float[] b);
|
||||
/**
|
||||
* Returns a singleton (stateless) {@link VectorUtilSupport} to support SIMD usage in {@link
|
||||
* VectorUtil}.
|
||||
*/
|
||||
public abstract VectorUtilSupport getVectorUtilSupport();
|
||||
|
||||
/** Returns the dot product computed over signed bytes. */
|
||||
int dotProduct(byte[] a, byte[] b);
|
||||
// *** Lookup mechanism: ***
|
||||
|
||||
/** Returns the cosine similarity between the two byte vectors. */
|
||||
float cosine(byte[] a, byte[] b);
|
||||
|
||||
/** Returns the sum of squared differences of the two byte vectors. */
|
||||
int squareDistance(byte[] a, byte[] b);
|
||||
|
||||
// -- provider lookup mechanism
|
||||
|
||||
static final Logger LOG = Logger.getLogger(VectorUtilProvider.class.getName());
|
||||
private static final Logger LOG = Logger.getLogger(VectorizationProvider.class.getName());
|
||||
|
||||
/** The minimal version of Java that has the bugfix for JDK-8301190. */
|
||||
static final Version VERSION_JDK8301190_FIXED = Version.parse("20.0.2");
|
||||
private static final Version VERSION_JDK8301190_FIXED = Version.parse("20.0.2");
|
||||
|
||||
static VectorUtilProvider lookup(boolean testMode) {
|
||||
// visible for tests
|
||||
static VectorizationProvider lookup(boolean testMode) {
|
||||
final int runtimeVersion = Runtime.version().feature();
|
||||
if (runtimeVersion >= 20 && runtimeVersion <= 21) {
|
||||
// is locale sane (only buggy in Java 20)
|
||||
|
@ -62,32 +78,34 @@ interface VectorUtilProvider {
|
|||
LOG.warning(
|
||||
"Java runtime is using a buggy default locale; Java vector incubator API can't be enabled: "
|
||||
+ Locale.getDefault());
|
||||
return new VectorUtilDefaultProvider();
|
||||
return new DefaultVectorizationProvider();
|
||||
}
|
||||
// is the incubator module present and readable (JVM providers may to exclude them or it is
|
||||
// build with jlink)
|
||||
if (!vectorModulePresentAndReadable()) {
|
||||
LOG.warning(
|
||||
"Java vector incubator module is not readable. For optimal vector performance, pass '--add-modules jdk.incubator.vector' to enable Vector API.");
|
||||
return new VectorUtilDefaultProvider();
|
||||
return new DefaultVectorizationProvider();
|
||||
}
|
||||
if (!testMode && isClientVM()) {
|
||||
LOG.warning("C2 compiler is disabled; Java vector incubator API can't be enabled");
|
||||
return new VectorUtilDefaultProvider();
|
||||
return new DefaultVectorizationProvider();
|
||||
}
|
||||
try {
|
||||
// we use method handles with lookup, so we do not need to deal with setAccessible as we
|
||||
// have private access through the lookup:
|
||||
final var lookup = MethodHandles.lookup();
|
||||
final var cls = lookup.findClass("org.apache.lucene.util.VectorUtilPanamaProvider");
|
||||
final var cls =
|
||||
lookup.findClass(
|
||||
"org.apache.lucene.internal.vectorization.PanamaVectorizationProvider");
|
||||
final var constr =
|
||||
lookup.findConstructor(cls, MethodType.methodType(void.class, boolean.class));
|
||||
try {
|
||||
return (VectorUtilProvider) constr.invoke(testMode);
|
||||
return (VectorizationProvider) constr.invoke(testMode);
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
// not supported because preferred vector size too small or similar
|
||||
LOG.warning("Java vector incubator API was not enabled. " + uoe.getMessage());
|
||||
return new VectorUtilDefaultProvider();
|
||||
return new DefaultVectorizationProvider();
|
||||
} catch (RuntimeException | Error e) {
|
||||
throw e;
|
||||
} catch (Throwable th) {
|
||||
|
@ -95,15 +113,15 @@ interface VectorUtilProvider {
|
|||
}
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
throw new LinkageError(
|
||||
"VectorUtilPanamaProvider is missing correctly typed constructor", e);
|
||||
"PanamaVectorizationProvider is missing correctly typed constructor", e);
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
throw new LinkageError("VectorUtilPanamaProvider is missing in Lucene JAR file", cnfe);
|
||||
throw new LinkageError("PanamaVectorizationProvider is missing in Lucene JAR file", cnfe);
|
||||
}
|
||||
} else if (runtimeVersion >= 22) {
|
||||
LOG.warning(
|
||||
"You are running with Java 22 or later. To make full use of the Vector API, please update Apache Lucene.");
|
||||
}
|
||||
return new VectorUtilDefaultProvider();
|
||||
return new DefaultVectorizationProvider();
|
||||
}
|
||||
|
||||
private static boolean vectorModulePresentAndReadable() {
|
||||
|
@ -112,7 +130,7 @@ interface VectorUtilProvider {
|
|||
.filter(m -> m.getName().equals("jdk.incubator.vector"))
|
||||
.findFirst();
|
||||
if (opt.isPresent()) {
|
||||
VectorUtilProvider.class.getModule().addReads(opt.get());
|
||||
VectorizationProvider.class.getModule().addReads(opt.get());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -143,4 +161,32 @@ interface VectorUtilProvider {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isValidCaller(String cn) {
|
||||
// add any class that is allowed to call getInstance()
|
||||
// NOTE: the list here is lazy
|
||||
return Stream.of(VectorUtil.class).map(Class::getName).anyMatch(cn::equals);
|
||||
}
|
||||
|
||||
private static void ensureCaller() {
|
||||
final boolean validCaller =
|
||||
StackWalker.getInstance()
|
||||
.walk(
|
||||
s ->
|
||||
s.skip(2)
|
||||
.limit(1)
|
||||
.map(StackFrame::getClassName)
|
||||
.allMatch(VectorizationProvider::isValidCaller));
|
||||
if (!validCaller) {
|
||||
throw new UnsupportedOperationException(
|
||||
"VectorizationProvider is internal and can only be used by known Lucene classes.");
|
||||
}
|
||||
}
|
||||
|
||||
/** This static holder class prevents classloading deadlock. */
|
||||
private static final class Holder {
|
||||
private Holder() {}
|
||||
|
||||
static final VectorizationProvider INSTANCE = lookup(false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Internal implementations to support SIMD vectorization. This package is for internal Lucene use
|
||||
* only!
|
||||
*
|
||||
* @see org.apache.lucene.internal.vectorization.VectorizationProvider
|
||||
*/
|
||||
package org.apache.lucene.internal.vectorization;
|
|
@ -17,10 +17,14 @@
|
|||
|
||||
package org.apache.lucene.util;
|
||||
|
||||
import org.apache.lucene.internal.vectorization.VectorUtilSupport;
|
||||
import org.apache.lucene.internal.vectorization.VectorizationProvider;
|
||||
|
||||
/** Utilities for computations with numeric arrays */
|
||||
public final class VectorUtil {
|
||||
|
||||
private static final VectorUtilProvider PROVIDER = VectorUtilProvider.lookup(false);
|
||||
private static final VectorUtilSupport IMPL =
|
||||
VectorizationProvider.getInstance().getVectorUtilSupport();
|
||||
|
||||
private VectorUtil() {}
|
||||
|
||||
|
@ -33,7 +37,7 @@ public final class VectorUtil {
|
|||
if (a.length != b.length) {
|
||||
throw new IllegalArgumentException("vector dimensions differ: " + a.length + "!=" + b.length);
|
||||
}
|
||||
float r = PROVIDER.dotProduct(a, b);
|
||||
float r = IMPL.dotProduct(a, b);
|
||||
assert Float.isFinite(r);
|
||||
return r;
|
||||
}
|
||||
|
@ -47,7 +51,7 @@ public final class VectorUtil {
|
|||
if (a.length != b.length) {
|
||||
throw new IllegalArgumentException("vector dimensions differ: " + a.length + "!=" + b.length);
|
||||
}
|
||||
float r = PROVIDER.cosine(a, b);
|
||||
float r = IMPL.cosine(a, b);
|
||||
assert Float.isFinite(r);
|
||||
return r;
|
||||
}
|
||||
|
@ -57,7 +61,7 @@ public final class VectorUtil {
|
|||
if (a.length != b.length) {
|
||||
throw new IllegalArgumentException("vector dimensions differ: " + a.length + "!=" + b.length);
|
||||
}
|
||||
return PROVIDER.cosine(a, b);
|
||||
return IMPL.cosine(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,7 +73,7 @@ public final class VectorUtil {
|
|||
if (a.length != b.length) {
|
||||
throw new IllegalArgumentException("vector dimensions differ: " + a.length + "!=" + b.length);
|
||||
}
|
||||
float r = PROVIDER.squareDistance(a, b);
|
||||
float r = IMPL.squareDistance(a, b);
|
||||
assert Float.isFinite(r);
|
||||
return r;
|
||||
}
|
||||
|
@ -79,7 +83,7 @@ public final class VectorUtil {
|
|||
if (a.length != b.length) {
|
||||
throw new IllegalArgumentException("vector dimensions differ: " + a.length + "!=" + b.length);
|
||||
}
|
||||
return PROVIDER.squareDistance(a, b);
|
||||
return IMPL.squareDistance(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,7 +148,7 @@ public final class VectorUtil {
|
|||
if (a.length != b.length) {
|
||||
throw new IllegalArgumentException("vector dimensions differ: " + a.length + "!=" + b.length);
|
||||
}
|
||||
return PROVIDER.dotProduct(a, b);
|
||||
return IMPL.dotProduct(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,11 +14,8 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.util;
|
||||
package org.apache.lucene.internal.vectorization;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.logging.Logger;
|
||||
import jdk.incubator.vector.ByteVector;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.IntVector;
|
||||
|
@ -28,8 +25,7 @@ import jdk.incubator.vector.VectorOperators;
|
|||
import jdk.incubator.vector.VectorShape;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/** A VectorUtil provider implementation that leverages the Panama Vector API. */
|
||||
final class VectorUtilPanamaProvider implements VectorUtilProvider {
|
||||
final class PanamaVectorUtilSupport implements VectorUtilSupport {
|
||||
|
||||
private static final int INT_SPECIES_PREF_BIT_SIZE = IntVector.SPECIES_PREFERRED.vectorBitSize();
|
||||
|
||||
|
@ -37,14 +33,6 @@ final class VectorUtilPanamaProvider implements VectorUtilProvider {
|
|||
private static final VectorSpecies<Byte> PREF_BYTE_SPECIES;
|
||||
private static final VectorSpecies<Short> PREF_SHORT_SPECIES;
|
||||
|
||||
/**
|
||||
* x86 and less than 256-bit vectors.
|
||||
*
|
||||
* <p>it could be that it has only AVX1 and integer vectors are fast. it could also be that it has
|
||||
* no AVX and integer vectors are extremely slow. don't use integer vectors to avoid landmines.
|
||||
*/
|
||||
private final boolean hasFastIntegerVectors;
|
||||
|
||||
static {
|
||||
if (INT_SPECIES_PREF_BIT_SIZE >= 256) {
|
||||
PREF_BYTE_SPECIES =
|
||||
|
@ -59,39 +47,10 @@ final class VectorUtilPanamaProvider implements VectorUtilProvider {
|
|||
}
|
||||
}
|
||||
|
||||
// Extracted to a method to be able to apply the SuppressForbidden annotation
|
||||
@SuppressWarnings("removal")
|
||||
@SuppressForbidden(reason = "security manager")
|
||||
private static <T> T doPrivileged(PrivilegedAction<T> action) {
|
||||
return AccessController.doPrivileged(action);
|
||||
}
|
||||
private final boolean useIntegerVectors;
|
||||
|
||||
VectorUtilPanamaProvider(boolean testMode) {
|
||||
if (!testMode && INT_SPECIES_PREF_BIT_SIZE < 128) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Vector bit size is less than 128: " + INT_SPECIES_PREF_BIT_SIZE);
|
||||
}
|
||||
|
||||
// hack to work around for JDK-8309727:
|
||||
try {
|
||||
doPrivileged(
|
||||
() ->
|
||||
FloatVector.fromArray(PREF_FLOAT_SPECIES, new float[PREF_FLOAT_SPECIES.length()], 0));
|
||||
} catch (SecurityException se) {
|
||||
throw new UnsupportedOperationException(
|
||||
"We hit initialization failure described in JDK-8309727: " + se);
|
||||
}
|
||||
|
||||
// check if the system is x86 and less than 256-bit vectors:
|
||||
var isAMD64withoutAVX2 = Constants.OS_ARCH.equals("amd64") && INT_SPECIES_PREF_BIT_SIZE < 256;
|
||||
this.hasFastIntegerVectors = testMode || false == isAMD64withoutAVX2;
|
||||
|
||||
var log = Logger.getLogger(getClass().getName());
|
||||
log.info(
|
||||
"Java vector incubator API enabled"
|
||||
+ (testMode ? " (test mode)" : "")
|
||||
+ "; uses preferredBitSize="
|
||||
+ INT_SPECIES_PREF_BIT_SIZE);
|
||||
PanamaVectorUtilSupport(boolean useIntegerVectors) {
|
||||
this.useIntegerVectors = useIntegerVectors;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -301,7 +260,7 @@ final class VectorUtilPanamaProvider implements VectorUtilProvider {
|
|||
int res = 0;
|
||||
// only vectorize if we'll at least enter the loop a single time, and we have at least 128-bit
|
||||
// vectors (256-bit on intel to dodge performance landmines)
|
||||
if (a.length >= 16 && hasFastIntegerVectors) {
|
||||
if (a.length >= 16 && useIntegerVectors) {
|
||||
// compute vectorized dot product consistent with VPDPBUSD instruction
|
||||
if (INT_SPECIES_PREF_BIT_SIZE >= 256) {
|
||||
// optimized 256/512 bit implementation, processes 8/16 bytes at a time
|
||||
|
@ -358,7 +317,7 @@ final class VectorUtilPanamaProvider implements VectorUtilProvider {
|
|||
int norm2 = 0;
|
||||
// only vectorize if we'll at least enter the loop a single time, and we have at least 128-bit
|
||||
// vectors (256-bit on intel to dodge performance landmines)
|
||||
if (a.length >= 16 && hasFastIntegerVectors) {
|
||||
if (a.length >= 16 && useIntegerVectors) {
|
||||
if (INT_SPECIES_PREF_BIT_SIZE >= 256) {
|
||||
// optimized 256/512 bit implementation, processes 8/16 bytes at a time
|
||||
int upperBound = PREF_BYTE_SPECIES.loopBound(a.length);
|
||||
|
@ -448,7 +407,7 @@ final class VectorUtilPanamaProvider implements VectorUtilProvider {
|
|||
int res = 0;
|
||||
// only vectorize if we'll at least enter the loop a single time, and we have at least 128-bit
|
||||
// vectors (256-bit on intel to dodge performance landmines)
|
||||
if (a.length >= 16 && hasFastIntegerVectors) {
|
||||
if (a.length >= 16 && useIntegerVectors) {
|
||||
if (INT_SPECIES_PREF_BIT_SIZE >= 256) {
|
||||
// optimized 256/512 bit implementation, processes 8/16 bytes at a time
|
||||
int upperBound = PREF_BYTE_SPECIES.loopBound(a.length);
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.internal.vectorization;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.logging.Logger;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.IntVector;
|
||||
import org.apache.lucene.util.Constants;
|
||||
import org.apache.lucene.util.SuppressForbidden;
|
||||
|
||||
/** A vectorization provider that leverages the Panama Vector API. */
|
||||
final class PanamaVectorizationProvider extends VectorizationProvider {
|
||||
|
||||
private final VectorUtilSupport vectorUtilSupport;
|
||||
|
||||
/**
|
||||
* x86 and less than 256-bit vectors.
|
||||
*
|
||||
* <p>it could be that it has only AVX1 and integer vectors are fast. it could also be that it has
|
||||
* no AVX and integer vectors are extremely slow. don't use integer vectors to avoid landmines.
|
||||
*/
|
||||
private final boolean hasFastIntegerVectors;
|
||||
|
||||
// Extracted to a method to be able to apply the SuppressForbidden annotation
|
||||
@SuppressWarnings("removal")
|
||||
@SuppressForbidden(reason = "security manager")
|
||||
private static <T> T doPrivileged(PrivilegedAction<T> action) {
|
||||
return AccessController.doPrivileged(action);
|
||||
}
|
||||
|
||||
PanamaVectorizationProvider(boolean testMode) {
|
||||
final int intPreferredBitSize = IntVector.SPECIES_PREFERRED.vectorBitSize();
|
||||
if (!testMode && intPreferredBitSize < 128) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Vector bit size is less than 128: " + intPreferredBitSize);
|
||||
}
|
||||
|
||||
// hack to work around for JDK-8309727:
|
||||
try {
|
||||
doPrivileged(
|
||||
() ->
|
||||
FloatVector.fromArray(
|
||||
FloatVector.SPECIES_PREFERRED,
|
||||
new float[FloatVector.SPECIES_PREFERRED.length()],
|
||||
0));
|
||||
} catch (SecurityException se) {
|
||||
throw new UnsupportedOperationException(
|
||||
"We hit initialization failure described in JDK-8309727: " + se);
|
||||
}
|
||||
|
||||
// check if the system is x86 and less than 256-bit vectors:
|
||||
var isAMD64withoutAVX2 = Constants.OS_ARCH.equals("amd64") && intPreferredBitSize < 256;
|
||||
this.hasFastIntegerVectors = testMode || false == isAMD64withoutAVX2;
|
||||
|
||||
this.vectorUtilSupport = new PanamaVectorUtilSupport(hasFastIntegerVectors);
|
||||
|
||||
var log = Logger.getLogger(getClass().getName());
|
||||
log.info(
|
||||
"Java vector incubator API enabled"
|
||||
+ (testMode ? " (test mode)" : "")
|
||||
+ "; uses preferredBitSize="
|
||||
+ intPreferredBitSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VectorUtilSupport getVectorUtilSupport() {
|
||||
return vectorUtilSupport;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.internal.vectorization;
|
||||
|
||||
import org.apache.lucene.tests.util.LuceneTestCase;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
public abstract class BaseVectorizationTestCase extends LuceneTestCase {
|
||||
|
||||
protected static final VectorizationProvider LUCENE_PROVIDER = new DefaultVectorizationProvider();
|
||||
protected static final VectorizationProvider PANAMA_PROVIDER = VectorizationProvider.lookup(true);
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
assumeTrue(
|
||||
"Test only works when JDK's vector incubator module is enabled.",
|
||||
PANAMA_PROVIDER.getClass() != LUCENE_PROVIDER.getClass());
|
||||
}
|
||||
}
|
|
@ -14,20 +14,16 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.util;
|
||||
package org.apache.lucene.internal.vectorization;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
|
||||
import java.util.function.ToDoubleFunction;
|
||||
import java.util.function.ToIntFunction;
|
||||
import java.util.stream.IntStream;
|
||||
import org.apache.lucene.tests.util.LuceneTestCase;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
public class TestVectorUtilProviders extends LuceneTestCase {
|
||||
public class TestVectorUtilSupport extends BaseVectorizationTestCase {
|
||||
|
||||
private static final double DELTA = 1e-3;
|
||||
private static final VectorUtilProvider LUCENE_PROVIDER = new VectorUtilDefaultProvider();
|
||||
private static final VectorUtilProvider JDK_PROVIDER = VectorUtilProvider.lookup(true);
|
||||
|
||||
private static final int[] VECTOR_SIZES = {
|
||||
1, 4, 6, 8, 13, 16, 25, 32, 64, 100, 128, 207, 256, 300, 512, 702, 1024
|
||||
|
@ -35,7 +31,7 @@ public class TestVectorUtilProviders extends LuceneTestCase {
|
|||
|
||||
private final int size;
|
||||
|
||||
public TestVectorUtilProviders(int size) {
|
||||
public TestVectorUtilSupport(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
|
@ -44,13 +40,6 @@ public class TestVectorUtilProviders extends LuceneTestCase {
|
|||
return () -> IntStream.of(VECTOR_SIZES).boxed().map(i -> new Object[] {i}).iterator();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
assumeFalse(
|
||||
"Test only works when JDK's vector incubator module is enabled.",
|
||||
JDK_PROVIDER instanceof VectorUtilDefaultProvider);
|
||||
}
|
||||
|
||||
public void testFloatVectors() {
|
||||
var a = new float[size];
|
||||
var b = new float[size];
|
||||
|
@ -73,11 +62,16 @@ public class TestVectorUtilProviders extends LuceneTestCase {
|
|||
assertFloatReturningProviders(p -> p.cosine(a, b));
|
||||
}
|
||||
|
||||
private void assertFloatReturningProviders(ToDoubleFunction<VectorUtilProvider> func) {
|
||||
assertEquals(func.applyAsDouble(LUCENE_PROVIDER), func.applyAsDouble(JDK_PROVIDER), DELTA);
|
||||
private void assertFloatReturningProviders(ToDoubleFunction<VectorUtilSupport> func) {
|
||||
assertEquals(
|
||||
func.applyAsDouble(LUCENE_PROVIDER.getVectorUtilSupport()),
|
||||
func.applyAsDouble(PANAMA_PROVIDER.getVectorUtilSupport()),
|
||||
DELTA);
|
||||
}
|
||||
|
||||
private void assertIntReturningProviders(ToIntFunction<VectorUtilProvider> func) {
|
||||
assertEquals(func.applyAsInt(LUCENE_PROVIDER), func.applyAsInt(JDK_PROVIDER));
|
||||
private void assertIntReturningProviders(ToIntFunction<VectorUtilSupport> func) {
|
||||
assertEquals(
|
||||
func.applyAsInt(LUCENE_PROVIDER.getVectorUtilSupport()),
|
||||
func.applyAsInt(PANAMA_PROVIDER.getVectorUtilSupport()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.internal.vectorization;
|
||||
|
||||
import org.apache.lucene.tests.util.LuceneTestCase;
|
||||
|
||||
public class TestVectorizationProvider extends LuceneTestCase {
|
||||
|
||||
public void testCallerOfGetter() {
|
||||
expectThrows(UnsupportedOperationException.class, TestVectorizationProvider::illegalCaller);
|
||||
}
|
||||
|
||||
private static void illegalCaller() {
|
||||
VectorizationProvider.getInstance();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue