From fde2e50d9e8644c5a5f2305c93512d1fb312a6e3 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Mon, 3 Jul 2023 11:39:19 +0200 Subject: [PATCH] Refactor vectorization support in Lucene (#12410) --- lucene/CHANGES.txt | 3 + .../DefaultVectorUtilSupport.java} | 7 +- .../DefaultVectorizationProvider.java | 33 ++++++ .../vectorization/VectorUtilSupport.java | 44 +++++++ .../vectorization/VectorizationProvider.java} | 110 +++++++++++++----- .../internal/vectorization/package-info.java | 24 ++++ .../org/apache/lucene/util/VectorUtil.java | 18 +-- .../PanamaVectorUtilSupport.java} | 57 ++------- .../PanamaVectorizationProvider.java | 85 ++++++++++++++ .../BaseVectorizationTestCase.java | 33 ++++++ .../vectorization/TestVectorUtilSupport.java} | 30 ++--- .../TestVectorizationProvider.java | 30 +++++ 12 files changed, 364 insertions(+), 110 deletions(-) rename lucene/core/src/java/org/apache/lucene/{util/VectorUtilDefaultProvider.java => internal/vectorization/DefaultVectorUtilSupport.java} (96%) create mode 100644 lucene/core/src/java/org/apache/lucene/internal/vectorization/DefaultVectorizationProvider.java create mode 100644 lucene/core/src/java/org/apache/lucene/internal/vectorization/VectorUtilSupport.java rename lucene/core/src/java/org/apache/lucene/{util/VectorUtilProvider.java => internal/vectorization/VectorizationProvider.java} (57%) create mode 100644 lucene/core/src/java/org/apache/lucene/internal/vectorization/package-info.java rename lucene/core/src/java20/org/apache/lucene/{util/VectorUtilPanamaProvider.java => internal/vectorization/PanamaVectorUtilSupport.java} (91%) create mode 100644 lucene/core/src/java20/org/apache/lucene/internal/vectorization/PanamaVectorizationProvider.java create mode 100644 lucene/core/src/test/org/apache/lucene/internal/vectorization/BaseVectorizationTestCase.java rename lucene/core/src/test/org/apache/lucene/{util/TestVectorUtilProviders.java => internal/vectorization/TestVectorUtilSupport.java} (72%) create mode 100644 lucene/core/src/test/org/apache/lucene/internal/vectorization/TestVectorizationProvider.java diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index 33e8a098d71..f45ed636d40 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -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 diff --git a/lucene/core/src/java/org/apache/lucene/util/VectorUtilDefaultProvider.java b/lucene/core/src/java/org/apache/lucene/internal/vectorization/DefaultVectorUtilSupport.java similarity index 96% rename from lucene/core/src/java/org/apache/lucene/util/VectorUtilDefaultProvider.java rename to lucene/core/src/java/org/apache/lucene/internal/vectorization/DefaultVectorUtilSupport.java index 665181e8678..de546c9269a 100644 --- a/lucene/core/src/java/org/apache/lucene/util/VectorUtilDefaultProvider.java +++ b/lucene/core/src/java/org/apache/lucene/internal/vectorization/DefaultVectorUtilSupport.java @@ -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) { diff --git a/lucene/core/src/java/org/apache/lucene/internal/vectorization/DefaultVectorizationProvider.java b/lucene/core/src/java/org/apache/lucene/internal/vectorization/DefaultVectorizationProvider.java new file mode 100644 index 00000000000..f3d9aa95fd3 --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/internal/vectorization/DefaultVectorizationProvider.java @@ -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; + } +} diff --git a/lucene/core/src/java/org/apache/lucene/internal/vectorization/VectorUtilSupport.java b/lucene/core/src/java/org/apache/lucene/internal/vectorization/VectorUtilSupport.java new file mode 100644 index 00000000000..44943473fa4 --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/internal/vectorization/VectorUtilSupport.java @@ -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); +} diff --git a/lucene/core/src/java/org/apache/lucene/util/VectorUtilProvider.java b/lucene/core/src/java/org/apache/lucene/internal/vectorization/VectorizationProvider.java similarity index 57% rename from lucene/core/src/java/org/apache/lucene/util/VectorUtilProvider.java rename to lucene/core/src/java/org/apache/lucene/internal/vectorization/VectorizationProvider.java index 3fd29c2bf34..e3421533eea 100644 --- a/lucene/core/src/java/org/apache/lucene/util/VectorUtilProvider.java +++ b/lucene/core/src/java/org/apache/lucene/internal/vectorization/VectorizationProvider.java @@ -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); + } } diff --git a/lucene/core/src/java/org/apache/lucene/internal/vectorization/package-info.java b/lucene/core/src/java/org/apache/lucene/internal/vectorization/package-info.java new file mode 100644 index 00000000000..44ca53119ca --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/internal/vectorization/package-info.java @@ -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; diff --git a/lucene/core/src/java/org/apache/lucene/util/VectorUtil.java b/lucene/core/src/java/org/apache/lucene/util/VectorUtil.java index 0921bb75a66..b8819082ba9 100644 --- a/lucene/core/src/java/org/apache/lucene/util/VectorUtil.java +++ b/lucene/core/src/java/org/apache/lucene/util/VectorUtil.java @@ -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); } /** diff --git a/lucene/core/src/java20/org/apache/lucene/util/VectorUtilPanamaProvider.java b/lucene/core/src/java20/org/apache/lucene/internal/vectorization/PanamaVectorUtilSupport.java similarity index 91% rename from lucene/core/src/java20/org/apache/lucene/util/VectorUtilPanamaProvider.java rename to lucene/core/src/java20/org/apache/lucene/internal/vectorization/PanamaVectorUtilSupport.java index a1a5a404223..1343593615b 100644 --- a/lucene/core/src/java20/org/apache/lucene/util/VectorUtilPanamaProvider.java +++ b/lucene/core/src/java20/org/apache/lucene/internal/vectorization/PanamaVectorUtilSupport.java @@ -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 PREF_BYTE_SPECIES; private static final VectorSpecies PREF_SHORT_SPECIES; - /** - * x86 and less than 256-bit vectors. - * - *

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 doPrivileged(PrivilegedAction 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); diff --git a/lucene/core/src/java20/org/apache/lucene/internal/vectorization/PanamaVectorizationProvider.java b/lucene/core/src/java20/org/apache/lucene/internal/vectorization/PanamaVectorizationProvider.java new file mode 100644 index 00000000000..3c802f45310 --- /dev/null +++ b/lucene/core/src/java20/org/apache/lucene/internal/vectorization/PanamaVectorizationProvider.java @@ -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. + * + *

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 doPrivileged(PrivilegedAction 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; + } +} diff --git a/lucene/core/src/test/org/apache/lucene/internal/vectorization/BaseVectorizationTestCase.java b/lucene/core/src/test/org/apache/lucene/internal/vectorization/BaseVectorizationTestCase.java new file mode 100644 index 00000000000..34a0e523002 --- /dev/null +++ b/lucene/core/src/test/org/apache/lucene/internal/vectorization/BaseVectorizationTestCase.java @@ -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()); + } +} diff --git a/lucene/core/src/test/org/apache/lucene/util/TestVectorUtilProviders.java b/lucene/core/src/test/org/apache/lucene/internal/vectorization/TestVectorUtilSupport.java similarity index 72% rename from lucene/core/src/test/org/apache/lucene/util/TestVectorUtilProviders.java rename to lucene/core/src/test/org/apache/lucene/internal/vectorization/TestVectorUtilSupport.java index be1205890bd..440d4a65aa2 100644 --- a/lucene/core/src/test/org/apache/lucene/util/TestVectorUtilProviders.java +++ b/lucene/core/src/test/org/apache/lucene/internal/vectorization/TestVectorUtilSupport.java @@ -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 func) { - assertEquals(func.applyAsDouble(LUCENE_PROVIDER), func.applyAsDouble(JDK_PROVIDER), DELTA); + private void assertFloatReturningProviders(ToDoubleFunction func) { + assertEquals( + func.applyAsDouble(LUCENE_PROVIDER.getVectorUtilSupport()), + func.applyAsDouble(PANAMA_PROVIDER.getVectorUtilSupport()), + DELTA); } - private void assertIntReturningProviders(ToIntFunction func) { - assertEquals(func.applyAsInt(LUCENE_PROVIDER), func.applyAsInt(JDK_PROVIDER)); + private void assertIntReturningProviders(ToIntFunction func) { + assertEquals( + func.applyAsInt(LUCENE_PROVIDER.getVectorUtilSupport()), + func.applyAsInt(PANAMA_PROVIDER.getVectorUtilSupport())); } } diff --git a/lucene/core/src/test/org/apache/lucene/internal/vectorization/TestVectorizationProvider.java b/lucene/core/src/test/org/apache/lucene/internal/vectorization/TestVectorizationProvider.java new file mode 100644 index 00000000000..7c7a3453e43 --- /dev/null +++ b/lucene/core/src/test/org/apache/lucene/internal/vectorization/TestVectorizationProvider.java @@ -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(); + } +}