Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate the Incubating Panama Vector API #12311

Merged
merged 59 commits into from
May 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
a02161a
Include the Panama Vector API stubs in the generated 19/20 api jars
ChrisHegarty May 18, 2023
aad69f9
first dotProduct implementation
ChrisHegarty May 18, 2023
ab6300b
fix dimensions check
ChrisHegarty May 18, 2023
d49100c
merge Robert's dotProduct implementation
ChrisHegarty May 18, 2023
a714486
remove public
ChrisHegarty May 18, 2023
39bbc4d
remove doPriv and restructure module lookup
ChrisHegarty May 18, 2023
3edee15
remove unnecessary jvmArgs
ChrisHegarty May 18, 2023
030babd
Move DefaultVectorUtilProvider to it's own top-level class - since it…
ChrisHegarty May 19, 2023
927f1c7
refactor the provider and impl into a separate package
ChrisHegarty May 19, 2023
99f7e41
move to non-exported internal package
ChrisHegarty May 19, 2023
7b58a64
disable assertions in jdk.incubator.vector.LaneType
ChrisHegarty May 19, 2023
abb5979
revert assertion disablement. Check locale is workable
ChrisHegarty May 19, 2023
45b660f
Make sure to use UTC timezone when creating ZIP, so file reproduces o…
uschindler May 19, 2023
89bc0b5
Merge branch 'main' into panama_vector
uschindler May 19, 2023
0779fcb
simplify locale sensitive check - as per Uwe's suggestion
ChrisHegarty May 19, 2023
d3fe388
move back into util package, and make package-private. Some minor ren…
ChrisHegarty May 19, 2023
27ea8f2
Better filter classes and configure modules to include by java version
uschindler May 20, 2023
568a0c0
Remove unused code
uschindler May 20, 2023
bd9f479
Remove comment
uschindler May 20, 2023
74e6eb3
Only enable the vector module in Java 20
uschindler May 20, 2023
d6ff862
Further simplify extractor (and fix a bug where supers and interfaces…
uschindler May 20, 2023
45c00ae
Use a TreeMap of class names instead of sorting files. This also make…
uschindler May 20, 2023
f0c36f7
Print error on misconfiguration
uschindler May 20, 2023
3ed660f
Add warnings when vector module is note enabled or Lucene is running …
uschindler May 20, 2023
f1d4aad
tidy
uschindler May 20, 2023
afc3d55
speed up vector computation: more efficient reduction, 4x unrolling
rmuir May 21, 2023
b051a52
spotless
rmuir May 21, 2023
3a6cb81
vectorize the dotproduct(byte[], byte[]), initial impl
rmuir May 22, 2023
026091b
speed up dotproduct(byte[],byte[]) on 128bit vectors
rmuir May 22, 2023
956d402
speed up avx512 dotproduct(byte[],byte[])
rmuir May 22, 2023
83c26d8
add vectorized binary euclidean and cosine functions
rmuir May 22, 2023
2ca381b
remove bad javadocs sentence
rmuir May 22, 2023
38fe5c5
add vectorized float euclidean and cosine impls
rmuir May 23, 2023
8708251
INT_SPECIES_PREFERRED_BIT_SIZE
ChrisHegarty May 23, 2023
11e6634
remove comments
ChrisHegarty May 23, 2023
97ebe5b
Cleanup and rename constants and move to top of class file
uschindler May 23, 2023
919f0e4
Add a test comparing both providers (if available)
uschindler May 23, 2023
c8e5b6a
DELTA should be double to match signatures
uschindler May 23, 2023
609fc9b
Fix wrongly-named test parameter
uschindler May 23, 2023
74a9782
disable when vector bit size < 128
ChrisHegarty May 23, 2023
32a2a58
Use a varhandle to read the preferred vector size (spares extra catch…
uschindler May 23, 2023
e174526
Change loglevel to INFO like MMapDir
uschindler May 23, 2023
3007a61
Move success logging to after finding constructor (calling ctor canno…
uschindler May 23, 2023
1681757
Move logging to constructor of provider (like in MMap)
uschindler May 23, 2023
f9711e9
Move all logic to the provider class. It thows UOE on constructor if …
uschindler May 23, 2023
87348cf
try to prevent using vector api if c2 is disabled. untested, please d…
rmuir May 23, 2023
2b57e7d
satisfy linter
rmuir May 23, 2023
5d0206c
Add accesscontroller when reading sysprop; refactor code
uschindler May 23, 2023
fc0a79a
on CI builds don't disable C2 and pass no extra args to VM
uschindler May 23, 2023
6ecf445
Remove check (we bail out in ctor already)
uschindler May 23, 2023
6a18ba3
JDK-8301190 is fixed in 20.0.2, only disable prior to that update
ChrisHegarty May 24, 2023
5a54f9e
spotless
ChrisHegarty May 24, 2023
e3ea49f
Restructure the "Turkish vector bug"
uschindler May 24, 2023
1cd15f3
Remove throws clause (relic)
uschindler May 24, 2023
e898401
don't use integer vectors on x86 unless we have AVX2 or AVX3
rmuir May 24, 2023
8da5432
Rename some gradle files and apijars
uschindler May 24, 2023
fe8abaf
Track references to superclasses and interfaces correctly
uschindler May 24, 2023
8a6a33c
add changes
uschindler May 24, 2023
dd4eaac
Merge remote-tracking branch 'remotes/origin/main' into panama_vector
uschindler May 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ apply from: file('gradle/ide/eclipse.gradle')
// (java, tests)
apply from: file('gradle/java/folder-layout.gradle')
apply from: file('gradle/java/javac.gradle')
apply from: file('gradle/java/memorysegment-mrjar.gradle')
apply from: file('gradle/java/core-mrjar.gradle')
apply from: file('gradle/testing/defaults-tests.gradle')
apply from: file('gradle/testing/randomization.gradle')
apply from: file('gradle/testing/fail-on-no-tests.gradle')
Expand Down Expand Up @@ -158,7 +158,7 @@ apply from: file('gradle/generation/javacc.gradle')
apply from: file('gradle/generation/forUtil.gradle')
apply from: file('gradle/generation/antlr.gradle')
apply from: file('gradle/generation/unicode-test-classes.gradle')
apply from: file('gradle/generation/panama-foreign.gradle')
apply from: file('gradle/generation/extract-jdk-apis.gradle')

apply from: file('gradle/datasets/external-datasets.gradle')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,17 @@

def resources = scriptResources(buildscript)

configure(rootProject) {
ext {
// also change this in extractor tool: ExtractForeignAPI
vectorIncubatorJavaVersions = [ JavaVersion.VERSION_20 ] as Set
}
}

configure(project(":lucene:core")) {
ext {
apijars = file('src/generated/jdk');
panamaJavaVersions = [ 19, 20 ]
mrjarJavaVersions = [ 19, 20 ]
}

configurations {
Expand All @@ -31,9 +38,9 @@ configure(project(":lucene:core")) {
apiextractor "org.ow2.asm:asm:${scriptDepVersions['asm']}"
}

for (jdkVersion : panamaJavaVersions) {
def task = tasks.create(name: "generatePanamaForeignApiJar${jdkVersion}", type: JavaExec) {
description "Regenerate the API-only JAR file with public Panama Foreign API from JDK ${jdkVersion}"
for (jdkVersion : mrjarJavaVersions) {
def task = tasks.create(name: "generateJdkApiJar${jdkVersion}", type: JavaExec) {
description "Regenerate the API-only JAR file with public Panama Foreign & Vector API from JDK ${jdkVersion}"
group "generation"

javaLauncher = javaToolchains.launcherFor {
Expand All @@ -45,21 +52,21 @@ configure(project(":lucene:core")) {
javaLauncher.get()
return true
} catch (Exception e) {
logger.warn('Launcher for Java {} is not available; skipping regeneration of Panama Foreign API JAR.', jdkVersion)
logger.warn('Launcher for Java {} is not available; skipping regeneration of Panama Foreign & Vector API JAR.', jdkVersion)
logger.warn('Error: {}', e.cause?.message)
logger.warn("Please make sure to point env 'JAVA{}_HOME' to exactly JDK version {} or enable Gradle toolchain auto-download.", jdkVersion, jdkVersion)
return false
}
}

classpath = configurations.apiextractor
mainClass = file("${resources}/ExtractForeignAPI.java") as String
mainClass = file("${resources}/ExtractJdkApis.java") as String
systemProperties = [
'user.timezone': 'UTC'
]
args = [
jdkVersion,
new File(apijars, "panama-foreign-jdk${jdkVersion}.apijar"),
new File(apijars, "jdk${jdkVersion}.apijar"),
]
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,21 @@
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

Expand All @@ -35,54 +45,100 @@
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

public final class ExtractForeignAPI {
public final class ExtractJdkApis {

private static final FileTime FIXED_FILEDATE = FileTime.from(Instant.parse("2022-01-01T00:00:00Z"));

static final Map<String,String> CLASSFILE_MATCHERS = Map.of(
"java.base", "glob:java/{lang/foreign/*,nio/channels/FileChannel}.class",
"jdk.incubator.vector", "glob:jdk/incubator/vector/*.class"
);

static final Map<Integer,List<String>> MODULES_TO_PROCESS = Map.of(
19, List.of("java.base"),
20, List.of("java.base", "jdk.incubator.vector")
);

public static void main(String... args) throws IOException {
if (args.length != 2) {
throw new IllegalArgumentException("Need two parameters: java version, output file");
}
if (Integer.parseInt(args[0]) != Runtime.version().feature()) {
Integer jdk = Integer.valueOf(args[0]);
if (jdk.intValue() != Runtime.version().feature()) {
throw new IllegalStateException("Incorrect java version: " + Runtime.version().feature());
}
if (!MODULES_TO_PROCESS.containsKey(jdk)) {
throw new IllegalArgumentException("No support to extract stubs from java version: " + jdk);
}
var outputPath = Paths.get(args[1]);
var javaBaseModule = Paths.get(URI.create("jrt:/")).resolve("java.base").toRealPath();
var fileMatcher = javaBaseModule.getFileSystem().getPathMatcher("glob:java/{lang/foreign/*,nio/channels/FileChannel}.class");
try (var out = new ZipOutputStream(Files.newOutputStream(outputPath)); var stream = Files.walk(javaBaseModule)) {
var filesToExtract = stream.map(javaBaseModule::relativize).filter(fileMatcher::matches).sorted().collect(Collectors.toList());

try (var out = new ZipOutputStream(Files.newOutputStream(outputPath))) {
for (String mod : MODULES_TO_PROCESS.get(jdk)) {
var modulePath = Paths.get(URI.create("jrt:/")).resolve(mod).toRealPath();
var moduleMatcher = modulePath.getFileSystem().getPathMatcher(CLASSFILE_MATCHERS.get(mod));
process(modulePath, moduleMatcher, out);
}
}
}

static void process(Path modulePath, PathMatcher fileMatcher, ZipOutputStream out) throws IOException {
var classesToInclude = new HashSet<String>();
var references = new HashMap<String, String[]>();
var processed = new TreeMap<String, byte[]>();
try (var stream = Files.walk(modulePath)) {
var filesToExtract = stream.map(modulePath::relativize).filter(fileMatcher::matches).toArray(Path[]::new);
System.out.println("Transforming " + filesToExtract.length + " class files in [" + modulePath + "]...");
for (Path relative : filesToExtract) {
System.out.println("Processing class file: " + relative);
try (var in = Files.newInputStream(javaBaseModule.resolve(relative))) {
final var reader = new ClassReader(in);
final var cw = new ClassWriter(0);
reader.accept(new Cleaner(cw), ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
out.putNextEntry(new ZipEntry(relative.toString()).setLastModifiedTime(FIXED_FILEDATE));
out.write(cw.toByteArray());
out.closeEntry();
try (var in = Files.newInputStream(modulePath.resolve(relative))) {
var reader = new ClassReader(in);
var cw = new ClassWriter(0);
var cleaner = new Cleaner(cw, classesToInclude, references);
reader.accept(cleaner, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
processed.put(reader.getClassName(), cw.toByteArray());
}
}
}
// recursively add all superclasses / interfaces of visible classes to classesToInclude:
for (Set<String> a = classesToInclude; !a.isEmpty();
a = a.stream().map(references::get).filter(Objects::nonNull).flatMap(Arrays::stream).collect(Collectors.toSet())) {
classesToInclude.addAll(a);
}
// remove all non-visible or not referenced classes:
processed.keySet().removeIf(Predicate.not(classesToInclude::contains));
System.out.println("Writing " + processed.size() + " visible classes for [" + modulePath + "]...");
for (var cls : processed.entrySet()) {
String cn = cls.getKey();
System.out.println("Writing stub for class: " + cn);
out.putNextEntry(new ZipEntry(cn.concat(".class")).setLastModifiedTime(FIXED_FILEDATE));
out.write(cls.getValue());
out.closeEntry();
}
}

static boolean isVisible(int access) {
return (access & (Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) != 0;
}

static class Cleaner extends ClassVisitor {
private static final String PREVIEW_ANN = "jdk/internal/javac/PreviewFeature";
private static final String PREVIEW_ANN_DESCR = Type.getObjectType(PREVIEW_ANN).getDescriptor();

private boolean completelyHidden = false;
private final Set<String> classesToInclude;
private final Map<String, String[]> references;

Cleaner(ClassWriter out) {
Cleaner(ClassWriter out, Set<String> classesToInclude, Map<String, String[]> references) {
super(Opcodes.ASM9, out);
}

private boolean isHidden(int access) {
return completelyHidden || (access & (Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) == 0;
this.classesToInclude = classesToInclude;
this.references = references;
}

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(Opcodes.V11, access, name, signature, superName, interfaces);
completelyHidden = isHidden(access);
if (isVisible(access)) {
classesToInclude.add(name);
}
references.put(name, Stream.concat(Stream.of(superName), Arrays.stream(interfaces)).toArray(String[]::new));
}

@Override
Expand All @@ -92,7 +148,7 @@ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {

@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
if (isHidden(access)) {
if (!isVisible(access)) {
return null;
}
return new FieldVisitor(Opcodes.ASM9, super.visitField(access, name, descriptor, signature, value)) {
Expand All @@ -105,7 +161,7 @@ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {

@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
if (isHidden(access)) {
if (!isVisible(access)) {
return null;
}
return new MethodVisitor(Opcodes.ASM9, super.visitMethod(access, name, descriptor, signature, exceptions)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
* limitations under the License.
*/

// Produce an MR-JAR with Java 19+ MemorySegment implementation for MMapDirectory
// Produce an MR-JAR with Java 19+ foreign and vector implementations

configure(project(":lucene:core")) {
plugins.withType(JavaPlugin) {
for (jdkVersion : panamaJavaVersions) {
for (jdkVersion : mrjarJavaVersions) {
sourceSets.create("main${jdkVersion}") {
java {
srcDirs = ["src/java${jdkVersion}"]
Expand All @@ -29,7 +29,7 @@ configure(project(":lucene:core")) {
dependencies.add("main${jdkVersion}Implementation", sourceSets.main.output)

tasks.named("compileMain${jdkVersion}Java").configure {
def apijar = new File(apijars, "panama-foreign-jdk${jdkVersion}.apijar")
def apijar = new File(apijars, "jdk${jdkVersion}.apijar")

inputs.file(apijar)

Expand All @@ -40,12 +40,14 @@ configure(project(":lucene:core")) {
"-Xlint:-options",
"--patch-module", "java.base=${apijar}",
"--add-exports", "java.base/java.lang.foreign=ALL-UNNAMED",
// for compilation we patch the incubator packages into java.base, this has no effect on resulting class files:
"--add-exports", "java.base/jdk.incubator.vector=ALL-UNNAMED",
]
}
}

tasks.named('jar').configure {
for (jdkVersion : panamaJavaVersions) {
for (jdkVersion : mrjarJavaVersions) {
into("META-INF/versions/${jdkVersion}") {
from sourceSets["main${jdkVersion}"].output
}
Expand Down
9 changes: 7 additions & 2 deletions gradle/testing/defaults-tests.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ allprojects {
description: "Number of forked test JVMs"],
[propName: 'tests.haltonfailure', value: true, description: "Halt processing on test failure."],
[propName: 'tests.jvmargs',
value: { -> propertyOrEnvOrDefault("tests.jvmargs", "TEST_JVM_ARGS", "-XX:TieredStopAtLevel=1 -XX:+UseParallelGC -XX:ActiveProcessorCount=1") },
value: { -> propertyOrEnvOrDefault("tests.jvmargs", "TEST_JVM_ARGS", isCIBuild ? "" : "-XX:TieredStopAtLevel=1 -XX:+UseParallelGC -XX:ActiveProcessorCount=1") },
description: "Arguments passed to each forked JVM."],
// Other settings.
[propName: 'tests.neverUpToDate', value: true,
Expand Down Expand Up @@ -119,11 +119,16 @@ allprojects {
if (rootProject.runtimeJavaVersion < JavaVersion.VERSION_16) {
jvmArgs '--illegal-access=deny'
}

// Lucene needs to optional modules at runtime, which we want to enforce for testing
// (if the runner JVM does not support them, it will fail tests):
jvmArgs '--add-modules', 'jdk.unsupported,jdk.management'

// Enable the vector incubator module on supported Java versions:
if (rootProject.vectorIncubatorJavaVersions.contains(rootProject.runtimeJavaVersion)) {
jvmArgs '--add-modules', 'jdk.incubator.vector'
}

uschindler marked this conversation as resolved.
Show resolved Hide resolved
def loggingConfigFile = layout.projectDirectory.file("${resources}/logging.properties")
def tempDir = layout.projectDirectory.dir(testsTmpDir.toString())
jvmArgumentProviders.add(
Expand Down
9 changes: 9 additions & 0 deletions lucene/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ New Features

* GITHUB#12257: Create OnHeapHnswGraphSearcher to let OnHeapHnswGraph to be searched in a thread-safety manner. (Patrick Zhai)

* GITHUB#12302, GITHUB#12311: Add vectorized implementations of VectorUtil.dotProduct(),
squareDistance(), cosine() with Java 20 jdk.incubator.vector APIs. Applications started
with command line parameter "java --add-modules jdk.incubator.vector" on exactly Java 20
will automatically use the new vectorized implementations if running on a supported platform
(x86 AVX2 or later, ARM SVE or later). This is an opt-in feature and requires explicit Java
command line flag! When enabled, Lucene logs a notice using java.util.logging. Please test
thoroughly and report bugs/slowness to Lucene's mailing list.
(Chris Hegarty, Robert Muir, Uwe Schindler)

Improvements
---------------------

Expand Down
2 changes: 1 addition & 1 deletion lucene/core/src/generated/jdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ to point the Lucene build system to missing JDK versions. The regeneration task
a warning if a specific JDK is missing, leaving the already existing `.apijar` file
untouched.

The extraction is done with the ASM library, see `ExtractForeignAPI.java` source code.
The extraction is done with the ASM library, see `ExtractJdkApis.java` source code.
Binary file not shown.
Binary file added lucene/core/src/generated/jdk/jdk20.apijar
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ public float compare(byte[] v1, byte[] v2) {

/**
* Calculates a similarity score between the two vectors with a specified function. Higher
* similarity scores correspond to closer vectors. The offsets and lengths of the BytesRefs
* determine the vector data that is compared. Each (signed) byte represents a vector dimension.
* similarity scores correspond to closer vectors. Each (signed) byte represents a vector
* dimension.
*
* @param v1 a vector
* @param v2 another vector, of the same dimension
Expand Down
Loading