From 766463d890de62614f91e4a71e79a42a57738c7c Mon Sep 17 00:00:00 2001 From: Stefan Zeiger Date: Thu, 21 Nov 2024 20:24:33 +0100 Subject: [PATCH] Proto optimization --- .../BufferedInputUncheckedBenchmark.scala | 1 + .../perfio/BufferedOutputNumBenchmark.scala | 1 + .../BufferedOutputStringBenchmark.scala | 6 +- .../BufferedOutputUncheckedBenchmark.scala | 1 + .../scala/perfio/LineTokenizerBenchmark.scala | 1 + .../scala/perfio/TextOutputBenchmark.scala | 1 + ...rseBenchmark.scala => ReadBenchmark.scala} | 2 +- .../src/main/scala/ReadWriteBenchmark.scala | 71 +++++++++++ .../src/main/scala/SmallWriteBenchmark.scala | 114 ++++++++++++++++++ .../src/main/scala/WriteBenchmark.scala | 70 ++++++++--- .../proto/runtime/GeneratedMessage.java | 9 ++ .../java/perfio/proto/runtime/Runtime.java | 19 ++- .../perfio/protoapi/DescriptorProtos.java | 66 +++++----- .../java/perfio/protoapi/PluginProtos.java | 8 +- .../main/scala/perfio/proto/MessageNode.scala | 4 +- src/main/java/perfio/ArrayBufferedOutput.java | 2 + src/main/java/perfio/BufferUtil.java | 87 ------------- src/main/java/perfio/BufferedInput.java | 6 +- src/main/java/perfio/BufferedOutput.java | 10 +- src/main/java/perfio/DirectBufferedInput.java | 4 +- src/main/java/perfio/DirectLineTokenizer.java | 2 + src/main/java/perfio/HeapBufferedInput.java | 4 +- src/main/java/perfio/ScalarLineTokenizer.java | 2 + src/main/java/perfio/TextOutput.java | 2 + .../java/perfio/VectorizedLineTokenizer.java | 3 +- src/main/java/perfio/internal/BufferUtil.java | 87 +++++++++++++ .../{ => internal}/StringInternals.java | 4 +- .../perfio/{ => internal}/VectorSupport.java | 10 +- 28 files changed, 433 insertions(+), 164 deletions(-) rename proto-bench/src/main/scala/{ParseBenchmark.scala => ReadBenchmark.scala} (99%) create mode 100644 proto-bench/src/main/scala/ReadWriteBenchmark.scala create mode 100644 proto-bench/src/main/scala/SmallWriteBenchmark.scala create mode 100644 proto-runtime/src/main/java/perfio/proto/runtime/GeneratedMessage.java delete mode 100644 src/main/java/perfio/BufferUtil.java create mode 100644 src/main/java/perfio/internal/BufferUtil.java rename src/main/java/perfio/{ => internal}/StringInternals.java (98%) rename src/main/java/perfio/{ => internal}/VectorSupport.java (73%) diff --git a/bench/src/main/scala/perfio/BufferedInputUncheckedBenchmark.scala b/bench/src/main/scala/perfio/BufferedInputUncheckedBenchmark.scala index cfd9773..9e5c6f3 100644 --- a/bench/src/main/scala/perfio/BufferedInputUncheckedBenchmark.scala +++ b/bench/src/main/scala/perfio/BufferedInputUncheckedBenchmark.scala @@ -2,6 +2,7 @@ package perfio import org.openjdk.jmh.annotations.* import org.openjdk.jmh.infra.* +import perfio.internal.BufferUtil import java.util.concurrent.TimeUnit diff --git a/bench/src/main/scala/perfio/BufferedOutputNumBenchmark.scala b/bench/src/main/scala/perfio/BufferedOutputNumBenchmark.scala index dc31223..df6cab7 100644 --- a/bench/src/main/scala/perfio/BufferedOutputNumBenchmark.scala +++ b/bench/src/main/scala/perfio/BufferedOutputNumBenchmark.scala @@ -2,6 +2,7 @@ package perfio import org.openjdk.jmh.annotations.* import org.openjdk.jmh.infra.* +import perfio.internal.BufferUtil import java.io.* import java.nio.ByteBuffer diff --git a/bench/src/main/scala/perfio/BufferedOutputStringBenchmark.scala b/bench/src/main/scala/perfio/BufferedOutputStringBenchmark.scala index 27b9fc9..2b61f18 100644 --- a/bench/src/main/scala/perfio/BufferedOutputStringBenchmark.scala +++ b/bench/src/main/scala/perfio/BufferedOutputStringBenchmark.scala @@ -99,21 +99,21 @@ class BufferedOutputStringBenchmark extends BenchUtil: bh.consume(bout.getBuffer) @Benchmark - def array_FullyBufferedOutput_growing(bh: Blackhole): Unit = + def array_ArrayBufferedOutput_growing(bh: Blackhole): Unit = val out = BufferedOutput.growing() writeTo(out) bh.consume(out.buffer) bh.consume(out.length) @Benchmark - def array_FullyBufferedOutput_growing_preallocated(bh: Blackhole): Unit = + def array_ArrayBufferedOutput_growing_preallocated(bh: Blackhole): Unit = val out = BufferedOutput.growing(totalLength) writeTo(out) bh.consume(out.buffer) bh.consume(out.length) @Benchmark - def array_FullyBufferedOutput_fixed(bh: Blackhole): Unit = + def array_ArrayBufferedOutput_fixed(bh: Blackhole): Unit = val out = BufferedOutput.ofArray(new Array[Byte](totalLength)) writeTo(out) bh.consume(out.buffer) diff --git a/bench/src/main/scala/perfio/BufferedOutputUncheckedBenchmark.scala b/bench/src/main/scala/perfio/BufferedOutputUncheckedBenchmark.scala index 8889d7d..ed8f655 100644 --- a/bench/src/main/scala/perfio/BufferedOutputUncheckedBenchmark.scala +++ b/bench/src/main/scala/perfio/BufferedOutputUncheckedBenchmark.scala @@ -2,6 +2,7 @@ package perfio import org.openjdk.jmh.annotations.* import org.openjdk.jmh.infra.* +import perfio.internal.BufferUtil import java.util.concurrent.TimeUnit diff --git a/bench/src/main/scala/perfio/LineTokenizerBenchmark.scala b/bench/src/main/scala/perfio/LineTokenizerBenchmark.scala index 12edf84..fcbaedb 100644 --- a/bench/src/main/scala/perfio/LineTokenizerBenchmark.scala +++ b/bench/src/main/scala/perfio/LineTokenizerBenchmark.scala @@ -2,6 +2,7 @@ package perfio import org.openjdk.jmh.annotations.* import org.openjdk.jmh.infra.* +import perfio.internal.StringInternals import java.io.* import java.lang.foreign.MemorySegment diff --git a/bench/src/main/scala/perfio/TextOutputBenchmark.scala b/bench/src/main/scala/perfio/TextOutputBenchmark.scala index 73d8ec3..3e07cfa 100644 --- a/bench/src/main/scala/perfio/TextOutputBenchmark.scala +++ b/bench/src/main/scala/perfio/TextOutputBenchmark.scala @@ -2,6 +2,7 @@ package perfio import org.openjdk.jmh.annotations.* import org.openjdk.jmh.infra.* +import perfio.internal.StringInternals import java.io.* import java.nio.charset.Charset diff --git a/proto-bench/src/main/scala/ParseBenchmark.scala b/proto-bench/src/main/scala/ReadBenchmark.scala similarity index 99% rename from proto-bench/src/main/scala/ParseBenchmark.scala rename to proto-bench/src/main/scala/ReadBenchmark.scala index 2ab7aa3..6f0c604 100644 --- a/proto-bench/src/main/scala/ParseBenchmark.scala +++ b/proto-bench/src/main/scala/ReadBenchmark.scala @@ -18,7 +18,7 @@ import java.nio.file.{Files, Path} @Measurement(iterations = 7, time = 1) @OutputTimeUnit(TimeUnit.MICROSECONDS) @State(Scope.Benchmark) -class ParseBenchmark: +class ReadBenchmark: private var testData: Array[Byte] = null private val testDataFile = Path.of("src/main/resources/CodeGeneratorRequest-pack.bin") diff --git a/proto-bench/src/main/scala/ReadWriteBenchmark.scala b/proto-bench/src/main/scala/ReadWriteBenchmark.scala new file mode 100644 index 0000000..e41da80 --- /dev/null +++ b/proto-bench/src/main/scala/ReadWriteBenchmark.scala @@ -0,0 +1,71 @@ +package perfio; + +import com.google.protobuf.compiler.PluginProtos as GPluginProtos +import org.openjdk.jmh.annotations.* +import org.openjdk.jmh.infra.* +import perfio.protoapi.PluginProtos as PPluginProtos + +import java.io.{BufferedInputStream, BufferedOutputStream, ByteArrayOutputStream, FileInputStream, FileOutputStream} +import java.nio.ByteOrder +import java.nio.file.{Files, Path} +import java.util.concurrent.TimeUnit + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(value = 1, jvmArgsAppend = Array("-Xmx12g", "-Xss32M", "-XX:+UnlockExperimentalVMOptions", "-XX:+UseZGC", "--enable-native-access=ALL-UNNAMED", "--add-modules", "jdk.incubator.vector")) +@Threads(1) +@Warmup(iterations = 7, time = 1) +@Measurement(iterations = 7, time = 1) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) +class ReadWriteBenchmark: + + private var testData: Array[Byte] = null + private val testDataFile = Path.of("src/main/resources/CodeGeneratorRequest-pack.bin") + + @Setup(Level.Trial) + def buildTestData(): Unit = + testData = Files.readAllBytes(testDataFile) + assert(testData.length == 159366, s"testData.length ${testData.length}") + + @Benchmark + def array_google(bh: Blackhole): Unit = + val p = GPluginProtos.CodeGeneratorRequest.parseFrom(testData) + val out = new ByteArrayOutputStream() + p.writeTo(out) + out.close() + bh.consume(out.size()) + + @Benchmark + def array_perfio(bh: Blackhole): Unit = + val p = PPluginProtos.CodeGeneratorRequest.parseFrom(BufferedInput.ofArray(testData).order(ByteOrder.LITTLE_ENDIAN)) + val bo = BufferedOutput.growing(4096) + p.writeTo(bo) + bo.close() + bh.consume(bo.length()) + + @Benchmark + def file_google(bh: Blackhole): Unit = + val in = new BufferedInputStream(new FileInputStream(testDataFile.toFile)) + val p = GPluginProtos.CodeGeneratorRequest.parseFrom(in) + in.close() + val out = new BufferedOutputStream(new FileOutputStream("/dev/null")) + p.writeTo(out) + out.close() + + @Benchmark + def file_perfio(bh: Blackhole): Unit = + val in = BufferedInput.of(new FileInputStream(testDataFile.toFile)).order(ByteOrder.LITTLE_ENDIAN) + val p = PPluginProtos.CodeGeneratorRequest.parseFrom(in) + in.close() + val bo = BufferedOutput.ofFile(Path.of("/dev/null"), 8192) + p.writeTo(bo) + bo.close() + + @Benchmark + def file_perfio_mapped(bh: Blackhole): Unit = + val in = BufferedInput.ofMappedFile(testDataFile).order(ByteOrder.LITTLE_ENDIAN) + val p = PPluginProtos.CodeGeneratorRequest.parseFrom(in) + in.close() + val bo = BufferedOutput.ofFile(Path.of("/dev/null"), 8192) + p.writeTo(bo) + bo.close() diff --git a/proto-bench/src/main/scala/SmallWriteBenchmark.scala b/proto-bench/src/main/scala/SmallWriteBenchmark.scala new file mode 100644 index 0000000..de576fa --- /dev/null +++ b/proto-bench/src/main/scala/SmallWriteBenchmark.scala @@ -0,0 +1,114 @@ +package perfio + +import com.google.protobuf.compiler.PluginProtos as GPluginProtos +import com.google.protobuf.{CodedOutputStream, GeneratedMessage as GGeneratedMessage, Parser as GParser} +import org.openjdk.jmh.annotations.* +import org.openjdk.jmh.infra.* +import perfio.proto.runtime.GeneratedMessage as PGeneratedMessage +import perfio.protoapi.PluginProtos as PPluginProtos + +import java.io.{BufferedOutputStream, ByteArrayOutputStream, FileOutputStream, OutputStream} +import java.lang.invoke.{MethodHandles, MethodType} +import java.nio.file.Path +import java.util.concurrent.TimeUnit + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(value = 1, jvmArgsAppend = Array("-Xmx12g", "-Xss32M", "-XX:+UnlockExperimentalVMOptions", "-XX:+UseZGC", "--enable-native-access=ALL-UNNAMED", "--add-modules", "jdk.incubator.vector")) +@Threads(1) +@Warmup(iterations = 15, time = 1) +@Measurement(iterations = 10, time = 1) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) +class SmallWriteBenchmark: + + @Param(Array("version1000")) + var testCase: String = null + + private var gMessage: GGeneratedMessage = null + private var pMessage: PGeneratedMessage = null + private var count = 1; + + private def p2g(p: PGeneratedMessage, gp: GParser[? <: GGeneratedMessage]): GGeneratedMessage = { + val out = BufferedOutput.growing() + p.writeTo(out) + out.close() + gp.parseFrom(out.toInputStream) + } + + val clearMemoMethod = { + val lookup = MethodHandles.privateLookupIn(classOf[GGeneratedMessage], MethodHandles.lookup()) + lookup.findVirtual(classOf[GGeneratedMessage], "setMemoizedSerializedSize", MethodType.methodType(Void.TYPE, Integer.TYPE)) + } + + private def clearMemo(m: GGeneratedMessage): Unit = { + clearMemoMethod.invokeExact(m, -1) + } + + @Setup(Level.Trial) + def buildInvocationData(): Unit = + testCase match + case "version1000" => + pMessage = (new PPluginProtos.Version).setMajor(1).setMinor(2).setPatch(3) //.setSuffix("foo") + gMessage = p2g(pMessage, GPluginProtos.Version.parser()) + count = 1000 + case "versionString1000" => + pMessage = (new PPluginProtos.Version).setMajor(1).setMinor(2).setPatch(3).setSuffix("foo") + gMessage = p2g(pMessage, GPluginProtos.Version.parser()) + count = 1000 + case "versionString1" => + pMessage = (new PPluginProtos.Version).setMajor(1).setMinor(2).setPatch(3).setSuffix("foo") + gMessage = p2g(pMessage, GPluginProtos.Version.parser()) + count = 1 + case "strings" => + val r = new PPluginProtos.CodeGeneratorRequest + for(i <- 1 to 1000) r.addFileToGenerate(s"file $i") + pMessage = r + gMessage = p2g(pMessage, GPluginProtos.CodeGeneratorRequest.parser()) + count = 10 + case "nested" => + val r = new PPluginProtos.CodeGeneratorRequest + r.setCompilerVersion((new PPluginProtos.Version).setMajor(1).setMinor(2).setPatch(3).setSuffix("foo")) + pMessage = r + gMessage = p2g(pMessage, GPluginProtos.CodeGeneratorRequest.parser()) + count = 100 + + private def writeGoogle(out: OutputStream): Unit = + val c = CodedOutputStream.newInstance(out, 4096) + var i = 0 + while i < count do + //clearMemo(gMessage.asInstanceOf[GPluginProtos.CodeGeneratorRequest].getCompilerVersion) + gMessage.writeTo(c) + i += 1 + c.flush() + out.close() + + private def writePerfio(out: BufferedOutput): Unit = + var i = 0 + while i < count do + pMessage.writeTo(out) + i += 1 + out.close() + + @Benchmark + def array_google(bh: Blackhole): Unit = + val baos = new ByteArrayOutputStream() + writeGoogle(baos) + bh.consume(baos.size()) + + @Benchmark + def array_perfio(bh: Blackhole): Unit = + val bo = BufferedOutput.growing(4096) + writePerfio(bo) + bh.consume(bo.length()) + + @Benchmark + def file_google(bh: Blackhole): Unit = + val out = new BufferedOutputStream(new FileOutputStream("/dev/null")) + writeGoogle(out) + out.close() + + @Benchmark + def file_perfio(bh: Blackhole): Unit = + val bo = BufferedOutput.ofFile(Path.of("/dev/null"), 8192) + writePerfio(bo) + bo.close() diff --git a/proto-bench/src/main/scala/WriteBenchmark.scala b/proto-bench/src/main/scala/WriteBenchmark.scala index db5072a..0feec5d 100644 --- a/proto-bench/src/main/scala/WriteBenchmark.scala +++ b/proto-bench/src/main/scala/WriteBenchmark.scala @@ -1,59 +1,91 @@ package perfio -import org.openjdk.jmh.annotations._ -import org.openjdk.jmh.infra._ +import com.google.protobuf.{CodedOutputStream, GeneratedMessage as GGeneratedMessage, Parser as GParser} +import perfio.proto.runtime.GeneratedMessage as PGeneratedMessage +import org.openjdk.jmh.annotations.* +import org.openjdk.jmh.infra.* import java.util.concurrent.TimeUnit -import com.google.protobuf.compiler.{PluginProtos => GPluginProtos} -import perfio.protoapi.{PluginProtos => PPluginProtos} +import com.google.protobuf.compiler.PluginProtos as GPluginProtos +import perfio.protoapi.PluginProtos as PPluginProtos -import java.io.{BufferedOutputStream, ByteArrayOutputStream, FileOutputStream} +import java.io.{BufferedOutputStream, ByteArrayOutputStream, FileOutputStream, OutputStream} +import java.lang.invoke.{MethodHandles, MethodType} import java.nio.ByteOrder import java.nio.file.{Files, Path} +import scala.jdk.CollectionConverters._ + @BenchmarkMode(Array(Mode.AverageTime)) @Fork(value = 1, jvmArgsAppend = Array("-Xmx12g", "-Xss32M", "-XX:+UnlockExperimentalVMOptions", "-XX:+UseZGC", "--enable-native-access=ALL-UNNAMED", "--add-modules", "jdk.incubator.vector")) @Threads(1) -@Warmup(iterations = 7, time = 1) -@Measurement(iterations = 7, time = 1) +@Warmup(iterations = 15, time = 1) +@Measurement(iterations = 10, time = 1) @OutputTimeUnit(TimeUnit.MICROSECONDS) @State(Scope.Benchmark) class WriteBenchmark: - private var testData: Array[Byte] = null private val testDataFile = Path.of("src/main/resources/CodeGeneratorRequest-pack.bin") - private var gReq: GPluginProtos.CodeGeneratorRequest = null - private var pReq: PPluginProtos.CodeGeneratorRequest = null + private var testData: Array[Byte] = null + private var gMessage: GGeneratedMessage = null + private var pMessage: PGeneratedMessage = null + + private def p2g(p: PGeneratedMessage, gp: GParser[? <: GGeneratedMessage]): GGeneratedMessage = { + val out = BufferedOutput.growing() + p.writeTo(out) + out.close() + gp.parseFrom(out.toInputStream) + } @Setup(Level.Trial) - def buildTestData(): Unit = + def buildTrialData(): Unit = testData = Files.readAllBytes(testDataFile) assert(testData.length == 159366, s"testData.length ${testData.length}") - gReq = GPluginProtos.CodeGeneratorRequest.parseFrom(testData) - pReq = PPluginProtos.CodeGeneratorRequest.parseFrom(BufferedInput.ofArray(testData).order(ByteOrder.LITTLE_ENDIAN)) + val m = PPluginProtos.CodeGeneratorRequest.parseFrom(BufferedInput.ofArray(testData).order(ByteOrder.LITTLE_ENDIAN)) + val pfs = m.getProtoFileList.asScala.toArray + for i <- 1 to 10 do + pfs.foreach(m.addProtoFile) + val out = BufferedOutput.growing() + m.writeTo(out) + out.close() + testData = out.copyToByteArray() + + @Setup(Level.Invocation) + def buildInvocationData(): Unit = + gMessage = GPluginProtos.CodeGeneratorRequest.parseFrom(testData) + pMessage = PPluginProtos.CodeGeneratorRequest.parseFrom(BufferedInput.ofArray(testData).order(ByteOrder.LITTLE_ENDIAN)) + + private def writeGoogle(out: OutputStream): Unit = + val c = CodedOutputStream.newInstance(out, 4096) + gMessage.writeTo(c) + c.flush() + out.close() + + private def writePerfio(out: BufferedOutput): Unit = + var i = 0 + pMessage.writeTo(out) + out.close() @Benchmark def array_google(bh: Blackhole): Unit = val baos = new ByteArrayOutputStream() - gReq.writeTo(baos) - baos.close() + writeGoogle(baos) bh.consume(baos.size()) @Benchmark def array_perfio(bh: Blackhole): Unit = val bo = BufferedOutput.growing(4096) - pReq.writeTo(bo) - bo.close() + writePerfio(bo) bh.consume(bo.length()) @Benchmark def file_google(bh: Blackhole): Unit = val out = new BufferedOutputStream(new FileOutputStream("/dev/null")) - gReq.writeTo(out) + writeGoogle(out) out.close() @Benchmark def file_perfio(bh: Blackhole): Unit = val bo = BufferedOutput.ofFile(Path.of("/dev/null"), 8192) - pReq.writeTo(bo) + writePerfio(bo) bo.close() diff --git a/proto-runtime/src/main/java/perfio/proto/runtime/GeneratedMessage.java b/proto-runtime/src/main/java/perfio/proto/runtime/GeneratedMessage.java new file mode 100644 index 0000000..6cd4b2d --- /dev/null +++ b/proto-runtime/src/main/java/perfio/proto/runtime/GeneratedMessage.java @@ -0,0 +1,9 @@ +package perfio.proto.runtime; + +import perfio.BufferedOutput; + +import java.io.IOException; + +public abstract class GeneratedMessage { + public abstract void writeTo(BufferedOutput out) throws IOException; +} diff --git a/proto-runtime/src/main/java/perfio/proto/runtime/Runtime.java b/proto-runtime/src/main/java/perfio/proto/runtime/Runtime.java index 98e319e..3529362 100644 --- a/proto-runtime/src/main/java/perfio/proto/runtime/Runtime.java +++ b/proto-runtime/src/main/java/perfio/proto/runtime/Runtime.java @@ -2,6 +2,7 @@ import perfio.BufferedInput; import perfio.BufferedOutput; +import perfio.internal.StringInternals; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -103,12 +104,26 @@ public static void skip(BufferedInput in, int wireType) throws IOException { public static void writeDouble(BufferedOutput out, double d) throws IOException { out.float64(d); } public static void writeSInt32(BufferedOutput out, int i) throws IOException { writeVarint(out, sint32ToVarint(i)); } public static void writeSInt64(BufferedOutput out, long l) throws IOException { writeVarint(out, sint64ToVarint(l)); } + public static void writeLen(BufferedOutput out, long l) throws IOException { writeVarint(out, l); } + public static void writeBytes(BufferedOutput out, byte[] bytes) throws IOException { writeLen(out, bytes.length); out.write(bytes); } - public static void writeString(BufferedOutput out, String s) throws IOException { writeBytes(out, s.getBytes(StandardCharsets.UTF_8)); } - public static void writeLen(BufferedOutput out, long l) throws IOException { writeVarint(out, l); } + + public static void writeString(BufferedOutput out, String s) throws IOException { + var l = s.length(); + if(l == 0) out.int8((byte)0); + else { + if(StringInternals.isLatin1(s)) { + var v = StringInternals.value(s); + if(!StringInternals.hasNegatives(v, 0, v.length)) { + writeLen(out, v.length); + out.write(v); + } else writeBytes(out, s.getBytes(StandardCharsets.UTF_8)); + } else writeBytes(out, s.getBytes(StandardCharsets.UTF_8)); + } + } public static void writePackedInt32(BufferedOutput out, IntList l) throws IOException { int lenMin = varintSize(l.len), lenMax = varintSize(l.len*10); diff --git a/proto/src/main/java/perfio/protoapi/DescriptorProtos.java b/proto/src/main/java/perfio/protoapi/DescriptorProtos.java index 4e4cd95..4ff54c7 100644 --- a/proto/src/main/java/perfio/protoapi/DescriptorProtos.java +++ b/proto/src/main/java/perfio/protoapi/DescriptorProtos.java @@ -41,7 +41,7 @@ public static Edition valueOf(int number) { } } - public static final class FileDescriptorSet { + public static final class FileDescriptorSet extends perfio.proto.runtime.GeneratedMessage { private java.util.List _file = java.util.List.of(); private void _file_initMut() { @@ -88,7 +88,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.FileDescriptorSet m) { } } - public static final class FileDescriptorProto { + public static final class FileDescriptorProto extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -266,9 +266,9 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.FileDescriptorProto m) { } } - public static final class DescriptorProto { + public static final class DescriptorProto extends perfio.proto.runtime.GeneratedMessage { - public static final class ExtensionRange { + public static final class ExtensionRange extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -335,7 +335,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.DescriptorProto.ExtensionR } } - public static final class ReservedRange { + public static final class ReservedRange extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -542,7 +542,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.DescriptorProto m) { } } - public static final class ExtensionRangeOptions { + public static final class ExtensionRangeOptions extends perfio.proto.runtime.GeneratedMessage { public enum VerificationState { DECLARATION(0), @@ -559,7 +559,7 @@ public static VerificationState valueOf(int number) { } } - public static final class Declaration { + public static final class Declaration extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -719,7 +719,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.ExtensionRangeOptions m) { } } - public static final class FieldDescriptorProto { + public static final class FieldDescriptorProto extends perfio.proto.runtime.GeneratedMessage { public enum Type { TYPE_DOUBLE(1), @@ -915,7 +915,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.FieldDescriptorProto m) { } } - public static final class OneofDescriptorProto { + public static final class OneofDescriptorProto extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -972,9 +972,9 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.OneofDescriptorProto m) { } } - public static final class EnumDescriptorProto { + public static final class EnumDescriptorProto extends perfio.proto.runtime.GeneratedMessage { - public static final class EnumReservedRange { + public static final class EnumReservedRange extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -1121,7 +1121,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.EnumDescriptorProto m) { } } - public static final class EnumValueDescriptorProto { + public static final class EnumValueDescriptorProto extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -1187,7 +1187,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.EnumValueDescriptorProto m } } - public static final class ServiceDescriptorProto { + public static final class ServiceDescriptorProto extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -1256,7 +1256,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.ServiceDescriptorProto m) } } - public static final class MethodDescriptorProto { + public static final class MethodDescriptorProto extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -1347,7 +1347,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.MethodDescriptorProto m) { } } - public static final class FileOptions { + public static final class FileOptions extends perfio.proto.runtime.GeneratedMessage { public enum OptimizeMode { SPEED(1), @@ -1585,7 +1585,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.FileOptions m) { } } - public static final class MessageOptions { + public static final class MessageOptions extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -1691,7 +1691,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.MessageOptions m) { } } - public static final class FieldOptions { + public static final class FieldOptions extends perfio.proto.runtime.GeneratedMessage { public enum CType { STRING(0), @@ -1775,7 +1775,7 @@ public static OptionTargetType valueOf(int number) { } } - public static final class EditionDefault { + public static final class EditionDefault extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -1829,7 +1829,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.FieldOptions.EditionDefaul } } - public static final class FeatureSupport { + public static final class FeatureSupport extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -2071,7 +2071,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.FieldOptions m) { } } - public static final class OneofOptions { + public static final class OneofOptions extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -2132,7 +2132,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.OneofOptions m) { } } - public static final class EnumOptions { + public static final class EnumOptions extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -2220,7 +2220,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.EnumOptions m) { } } - public static final class EnumValueOptions { + public static final class EnumValueOptions extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -2310,7 +2310,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.EnumValueOptions m) { } } - public static final class ServiceOptions { + public static final class ServiceOptions extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -2380,7 +2380,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.ServiceOptions m) { } } - public static final class MethodOptions { + public static final class MethodOptions extends perfio.proto.runtime.GeneratedMessage { public enum IdempotencyLevel { IDEMPOTENCY_UNKNOWN(0), @@ -2475,9 +2475,9 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.MethodOptions m) { } } - public static final class UninterpretedOption { + public static final class UninterpretedOption extends perfio.proto.runtime.GeneratedMessage { - public static final class NamePart { + public static final class NamePart extends perfio.proto.runtime.GeneratedMessage { private java.lang.String _namePart = ""; public java.lang.String getNamePart() { return _namePart; } @@ -2628,7 +2628,7 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.UninterpretedOption m) { } } - public static final class FeatureSet { + public static final class FeatureSet extends perfio.proto.runtime.GeneratedMessage { public enum FieldPresence { FIELD_PRESENCE_UNKNOWN(0), @@ -2818,9 +2818,9 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.FeatureSet m) { } } - public static final class FeatureSetDefaults { + public static final class FeatureSetDefaults extends perfio.proto.runtime.GeneratedMessage { - public static final class FeatureSetEditionDefault { + public static final class FeatureSetEditionDefault extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -2952,9 +2952,9 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.FeatureSetDefaults m) { } } - public static final class SourceCodeInfo { + public static final class SourceCodeInfo extends perfio.proto.runtime.GeneratedMessage { - public static final class Location { + public static final class Location extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -3091,9 +3091,9 @@ else if(o instanceof perfio.protoapi.DescriptorProtos.SourceCodeInfo m) { } } - public static final class GeneratedCodeInfo { + public static final class GeneratedCodeInfo extends perfio.proto.runtime.GeneratedMessage { - public static final class Annotation { + public static final class Annotation extends perfio.proto.runtime.GeneratedMessage { public enum Semantic { NONE(0), diff --git a/proto/src/main/java/perfio/protoapi/PluginProtos.java b/proto/src/main/java/perfio/protoapi/PluginProtos.java index d7d3834..fde4a92 100644 --- a/proto/src/main/java/perfio/protoapi/PluginProtos.java +++ b/proto/src/main/java/perfio/protoapi/PluginProtos.java @@ -6,7 +6,7 @@ public final class PluginProtos { private PluginProtos() {} - public static final class Version { + public static final class Version extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -79,7 +79,7 @@ else if(o instanceof perfio.protoapi.PluginProtos.Version m) { } } - public static final class CodeGeneratorRequest { + public static final class CodeGeneratorRequest extends perfio.proto.runtime.GeneratedMessage { private int flags0; @@ -172,7 +172,7 @@ else if(o instanceof perfio.protoapi.PluginProtos.CodeGeneratorRequest m) { } } - public static final class CodeGeneratorResponse { + public static final class CodeGeneratorResponse extends perfio.proto.runtime.GeneratedMessage { public enum Feature { FEATURE_NONE(0), @@ -191,7 +191,7 @@ public static Feature valueOf(int number) { } } - public static final class File { + public static final class File extends perfio.proto.runtime.GeneratedMessage { private int flags0; diff --git a/proto/src/main/scala/perfio/proto/MessageNode.scala b/proto/src/main/scala/perfio/proto/MessageNode.scala index c00f6e0..7fea34d 100644 --- a/proto/src/main/scala/perfio/proto/MessageNode.scala +++ b/proto/src/main/scala/perfio/proto/MessageNode.scala @@ -1,7 +1,7 @@ package perfio.proto import perfio.protoapi.DescriptorProtos.DescriptorProto -import perfio.proto.runtime.Runtime +import perfio.proto.runtime.{GeneratedMessage, Runtime} import perfio.scalaapi.* import perfio.{BufferedInput, BufferedOutput} @@ -40,7 +40,7 @@ class MessageNode(desc: DescriptorProto, val parent: ParentNode) extends ParentN def emit(using toc: TextOutputContext): Printed = pm""" - |public static final class ${className} { + |public static final class ${className} extends ${classOf[GeneratedMessage]} { > ${Printed(enums)(_.emit)} > ${Printed(messages)(_.emit)} > ${Printed(if(flagFields.nonEmpty || oneOfs.nonEmpty) pm"")} diff --git a/src/main/java/perfio/ArrayBufferedOutput.java b/src/main/java/perfio/ArrayBufferedOutput.java index ea35197..fa2085f 100644 --- a/src/main/java/perfio/ArrayBufferedOutput.java +++ b/src/main/java/perfio/ArrayBufferedOutput.java @@ -1,5 +1,7 @@ package perfio; +import perfio.internal.BufferUtil; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; diff --git a/src/main/java/perfio/BufferUtil.java b/src/main/java/perfio/BufferUtil.java deleted file mode 100644 index 8825701..0000000 --- a/src/main/java/perfio/BufferUtil.java +++ /dev/null @@ -1,87 +0,0 @@ -package perfio; - -import java.io.IOException; -import java.lang.foreign.Arena; -import java.lang.foreign.MemorySegment; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.VarHandle; -import java.nio.ByteOrder; -import java.nio.channels.FileChannel; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; - -class BufferUtil { - private BufferUtil() {} - - /// Compute a new buffer size for the given size alignment, limited to [#SOFT_MAX_ARRAY_LENGTH]. - static int growBuffer(int current, int target, int align) { - int l = current; - while(l < target) { - l *= 2; - if(l < 0) l = Integer.MAX_VALUE; - } - return Math.min(l, SOFT_MAX_ARRAY_LENGTH) & -align; - } - - // From jdk.internal.util.ArraysSupport - static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8; - - static final VarHandle BA_LONG_BIG = bavh(Long.TYPE, true); - static final VarHandle BA_INT_BIG = bavh(Integer.TYPE, true); - static final VarHandle BA_SHORT_BIG = bavh(Short.TYPE, true); - static final VarHandle BA_CHAR_BIG = bavh(Character.TYPE, true); - static final VarHandle BA_DOUBLE_BIG = bavh(Double.TYPE, true); - static final VarHandle BA_FLOAT_BIG = bavh(Float.TYPE, true); - - static final VarHandle BA_LONG_LITTLE = bavh(Long.TYPE, false); - static final VarHandle BA_INT_LITTLE = bavh(Integer.TYPE, false); - static final VarHandle BA_SHORT_LITTLE = bavh(Short.TYPE, false); - static final VarHandle BA_CHAR_LITTLE = bavh(Character.TYPE, false); - static final VarHandle BA_DOUBLE_LITTLE = bavh(Double.TYPE, false); - static final VarHandle BA_FLOAT_LITTLE = bavh(Float.TYPE, false); - - private static VarHandle bavh(Class cls, boolean be) { - return MethodHandles.byteArrayViewVarHandle(cls.arrayType(), be ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); - } - - static final VarHandle BB_LONG_BIG = bbvh(Long.TYPE, true); - static final VarHandle BB_INT_BIG = bbvh(Integer.TYPE, true); - static final VarHandle BB_SHORT_BIG = bbvh(Short.TYPE, true); - static final VarHandle BB_CHAR_BIG = bbvh(Character.TYPE, true); - static final VarHandle BB_DOUBLE_BIG = bbvh(Double.TYPE, true); - static final VarHandle BB_FLOAT_BIG = bbvh(Float.TYPE, true); - - static final VarHandle BB_LONG_LITTLE = bbvh(Long.TYPE, false); - static final VarHandle BB_INT_LITTLE = bbvh(Integer.TYPE, false); - static final VarHandle BB_SHORT_LITTLE = bbvh(Short.TYPE, false); - static final VarHandle BB_CHAR_LITTLE = bbvh(Character.TYPE, false); - static final VarHandle BB_DOUBLE_LITTLE = bbvh(Double.TYPE, false); - static final VarHandle BB_FLOAT_LITTLE = bbvh(Float.TYPE, false); - - private static VarHandle bbvh(Class cls, boolean be) { - return MethodHandles.byteBufferViewVarHandle(cls.arrayType(), be ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); - } - - static MemorySegment mapReadOnlyFile(Path file) throws IOException { - var a = Arena.ofAuto(); - try (var ch = FileChannel.open(file, StandardOpenOption.READ)) { - return ch.map(FileChannel.MapMode.READ_ONLY, 0, ch.size(), a); - } - } - - static final boolean VECTOR_ENABLED; - static final int VECTOR_LENGTH; - static { - var e = false; - var vlen = 8; // minimum for computing a usable minimum buffer length - try { - // Just a sanity check. We accept any reasonable size. Even 64-bit vectors (SWAR) are faster than scalar. - // Hopefully this will guarantee that the preferred species is actually vectorized (which is not the case - // with the experimental preview API at the moment). - e = VectorSupport.SPECIES.length() >= 8 && !"true".equals(System.getProperty("perfio.disableVectorized")); - if(e) vlen = VectorSupport.SPECIES.length(); - } catch(NoClassDefFoundError t) {} - VECTOR_ENABLED = e; - VECTOR_LENGTH = vlen; - } -} diff --git a/src/main/java/perfio/BufferedInput.java b/src/main/java/perfio/BufferedInput.java index 406d3db..ca5b962 100644 --- a/src/main/java/perfio/BufferedInput.java +++ b/src/main/java/perfio/BufferedInput.java @@ -1,5 +1,7 @@ package perfio; +import perfio.internal.BufferUtil; + import java.io.Closeable; import java.io.EOFException; import java.io.IOException; @@ -132,12 +134,12 @@ private static DirectBufferedInput create(MemorySegment bbSegment, MemorySegment private int activeViewInitialBuffered = 0; private boolean detachOnClose, skipOnClose = false; long parentTotalOffset = 0L; - CloseableView closeableView= null; + CloseableView closeableView = null; abstract BufferedInput createEmptyView(); abstract void clearBuffer(); abstract void copyBufferFrom(BufferedInput b); - + /// Fill the buffer as much as possible without blocking (starting at [#lim]), but at least /// until `count` bytes are available starting at [#pos] even if this requires blocking or /// growing the buffer. Less data may only be made available when the end of the input has been diff --git a/src/main/java/perfio/BufferedOutput.java b/src/main/java/perfio/BufferedOutput.java index 258c6eb..816a1b3 100644 --- a/src/main/java/perfio/BufferedOutput.java +++ b/src/main/java/perfio/BufferedOutput.java @@ -1,5 +1,7 @@ package perfio; +import perfio.internal.StringInternals; + import java.io.*; import java.nio.ByteOrder; import java.nio.charset.Charset; @@ -10,7 +12,7 @@ import java.util.Arrays; import java.util.Objects; -import static perfio.BufferUtil.*; +import static perfio.internal.BufferUtil.*; /// BufferedOutput provides buffered streaming writes to an OutputStream or similar data sink. @@ -705,6 +707,12 @@ final class FlushingBufferedOutput extends CacheRootBufferedOutput { void flushUpstream() throws IOException { out.flush(); } + @Override + void closeUpstream() throws IOException { + super.closeUpstream(); + out.close(); + } + void flushBlocks(boolean forceFlush) throws IOException { while(next != this) { var b = next; diff --git a/src/main/java/perfio/DirectBufferedInput.java b/src/main/java/perfio/DirectBufferedInput.java index 661dcac..a1ddc7d 100644 --- a/src/main/java/perfio/DirectBufferedInput.java +++ b/src/main/java/perfio/DirectBufferedInput.java @@ -1,12 +1,14 @@ package perfio; +import perfio.internal.BufferUtil; + import java.io.Closeable; import java.io.IOException; import java.lang.foreign.MemorySegment; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.Charset; -import static perfio.BufferUtil.*; +import static perfio.internal.BufferUtil.*; // This could be a lot simpler if we didn't have to do pagination but ByteBuffer is limited // to 2 GB and direct MemorySegment access is much, much slower as of JDK 22. diff --git a/src/main/java/perfio/DirectLineTokenizer.java b/src/main/java/perfio/DirectLineTokenizer.java index 1bec1a1..40deeae 100644 --- a/src/main/java/perfio/DirectLineTokenizer.java +++ b/src/main/java/perfio/DirectLineTokenizer.java @@ -1,5 +1,7 @@ package perfio; +import perfio.internal.StringInternals; + import java.io.IOException; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; diff --git a/src/main/java/perfio/HeapBufferedInput.java b/src/main/java/perfio/HeapBufferedInput.java index d7be8cb..520910c 100644 --- a/src/main/java/perfio/HeapBufferedInput.java +++ b/src/main/java/perfio/HeapBufferedInput.java @@ -1,10 +1,12 @@ package perfio; +import perfio.internal.BufferUtil; + import java.io.Closeable; import java.io.IOException; import java.nio.charset.Charset; -import static perfio.BufferUtil.*; +import static perfio.internal.BufferUtil.*; sealed abstract class HeapBufferedInput extends BufferedInput permits StreamingHeapBufferedInput, SwitchingHeapBufferedInput { byte[] buf; diff --git a/src/main/java/perfio/ScalarLineTokenizer.java b/src/main/java/perfio/ScalarLineTokenizer.java index cabd232..a134b72 100644 --- a/src/main/java/perfio/ScalarLineTokenizer.java +++ b/src/main/java/perfio/ScalarLineTokenizer.java @@ -1,5 +1,7 @@ package perfio; +import perfio.internal.StringInternals; + import java.io.IOException; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; diff --git a/src/main/java/perfio/TextOutput.java b/src/main/java/perfio/TextOutput.java index 105e32c..bfc737e 100644 --- a/src/main/java/perfio/TextOutput.java +++ b/src/main/java/perfio/TextOutput.java @@ -1,5 +1,7 @@ package perfio; +import perfio.internal.StringInternals; + import java.io.Closeable; import java.io.EOFException; import java.io.Flushable; diff --git a/src/main/java/perfio/VectorizedLineTokenizer.java b/src/main/java/perfio/VectorizedLineTokenizer.java index 60c24f0..336a735 100644 --- a/src/main/java/perfio/VectorizedLineTokenizer.java +++ b/src/main/java/perfio/VectorizedLineTokenizer.java @@ -3,13 +3,14 @@ import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorMask; import jdk.incubator.vector.VectorOperators; +import perfio.internal.StringInternals; import java.io.IOException; import java.lang.foreign.MemorySegment; import java.nio.ByteOrder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import static perfio.VectorSupport.*; +import static perfio.internal.VectorSupport.*; /// A vectorized implementation of [[LineTokenizer]]. /// diff --git a/src/main/java/perfio/internal/BufferUtil.java b/src/main/java/perfio/internal/BufferUtil.java new file mode 100644 index 0000000..51cc9b4 --- /dev/null +++ b/src/main/java/perfio/internal/BufferUtil.java @@ -0,0 +1,87 @@ +package perfio.internal; + +import java.io.IOException; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; + +public class BufferUtil { + private BufferUtil() {} + + /// Compute a new buffer size for the given size alignment, limited to [#SOFT_MAX_ARRAY_LENGTH]. + public static int growBuffer(int current, int target, int align) { + int l = current; + while(l < target) { + l *= 2; + if(l < 0) l = Integer.MAX_VALUE; + } + return Math.min(l, SOFT_MAX_ARRAY_LENGTH) & -align; + } + + // From jdk.internal.util.ArraysSupport + public static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8; + + public static final VarHandle BA_LONG_BIG = bavh(Long.TYPE, true); + public static final VarHandle BA_INT_BIG = bavh(Integer.TYPE, true); + public static final VarHandle BA_SHORT_BIG = bavh(Short.TYPE, true); + public static final VarHandle BA_CHAR_BIG = bavh(Character.TYPE, true); + public static final VarHandle BA_DOUBLE_BIG = bavh(Double.TYPE, true); + public static final VarHandle BA_FLOAT_BIG = bavh(Float.TYPE, true); + + public static final VarHandle BA_LONG_LITTLE = bavh(Long.TYPE, false); + public static final VarHandle BA_INT_LITTLE = bavh(Integer.TYPE, false); + public static final VarHandle BA_SHORT_LITTLE = bavh(Short.TYPE, false); + public static final VarHandle BA_CHAR_LITTLE = bavh(Character.TYPE, false); + public static final VarHandle BA_DOUBLE_LITTLE = bavh(Double.TYPE, false); + public static final VarHandle BA_FLOAT_LITTLE = bavh(Float.TYPE, false); + + private static VarHandle bavh(Class cls, boolean be) { + return MethodHandles.byteArrayViewVarHandle(cls.arrayType(), be ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); + } + + public static final VarHandle BB_LONG_BIG = bbvh(Long.TYPE, true); + public static final VarHandle BB_INT_BIG = bbvh(Integer.TYPE, true); + public static final VarHandle BB_SHORT_BIG = bbvh(Short.TYPE, true); + public static final VarHandle BB_CHAR_BIG = bbvh(Character.TYPE, true); + public static final VarHandle BB_DOUBLE_BIG = bbvh(Double.TYPE, true); + public static final VarHandle BB_FLOAT_BIG = bbvh(Float.TYPE, true); + + public static final VarHandle BB_LONG_LITTLE = bbvh(Long.TYPE, false); + public static final VarHandle BB_INT_LITTLE = bbvh(Integer.TYPE, false); + public static final VarHandle BB_SHORT_LITTLE = bbvh(Short.TYPE, false); + public static final VarHandle BB_CHAR_LITTLE = bbvh(Character.TYPE, false); + public static final VarHandle BB_DOUBLE_LITTLE = bbvh(Double.TYPE, false); + public static final VarHandle BB_FLOAT_LITTLE = bbvh(Float.TYPE, false); + + private static VarHandle bbvh(Class cls, boolean be) { + return MethodHandles.byteBufferViewVarHandle(cls.arrayType(), be ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); + } + + public static MemorySegment mapReadOnlyFile(Path file) throws IOException { + var a = Arena.ofAuto(); + try (var ch = FileChannel.open(file, StandardOpenOption.READ)) { + return ch.map(FileChannel.MapMode.READ_ONLY, 0, ch.size(), a); + } + } + + public static final boolean VECTOR_ENABLED; + public static final int VECTOR_LENGTH; + static { + var e = false; + var vlen = 8; // minimum for computing a usable minimum buffer length + try { + // Just a sanity check. We accept any reasonable size. Even 64-bit vectors (SWAR) are faster than scalar. + // Hopefully this will guarantee that the preferred species is actually vectorized (which is not the case + // with the experimental preview API at the moment). + e = VectorSupport.SPECIES.length() >= 8 && !"true".equals(System.getProperty("perfio.disableVectorized")); + if(e) vlen = VectorSupport.SPECIES.length(); + } catch(NoClassDefFoundError t) {} + VECTOR_ENABLED = e; + VECTOR_LENGTH = vlen; + } +} diff --git a/src/main/java/perfio/StringInternals.java b/src/main/java/perfio/internal/StringInternals.java similarity index 98% rename from src/main/java/perfio/StringInternals.java rename to src/main/java/perfio/internal/StringInternals.java index b3fd5fe..5a72566 100644 --- a/src/main/java/perfio/StringInternals.java +++ b/src/main/java/perfio/internal/StringInternals.java @@ -1,4 +1,4 @@ -package perfio; +package perfio.internal; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -8,7 +8,7 @@ /// Accessors for private String methods for faster string encoding that can be used when running with /// `--add-opens java.base/java.lang=ALL-UNNAMED`. Lookup and fallback are implemented via load-time initialization /// of constant MethodHandles for optimal performance. -class StringInternals { +public class StringInternals { private StringInternals() {} private static final MethodHandle isLatin1MH, valueMH, hasNegativesMH, newStringMH; diff --git a/src/main/java/perfio/VectorSupport.java b/src/main/java/perfio/internal/VectorSupport.java similarity index 73% rename from src/main/java/perfio/VectorSupport.java rename to src/main/java/perfio/internal/VectorSupport.java index 0b56ec1..94b010e 100644 --- a/src/main/java/perfio/VectorSupport.java +++ b/src/main/java/perfio/internal/VectorSupport.java @@ -1,14 +1,14 @@ -package perfio; +package perfio.internal; import jdk.incubator.vector.ByteVector; import jdk.incubator.vector.VectorSpecies; -class VectorSupport { +public class VectorSupport { private VectorSupport() {} - static final VectorSpecies SPECIES; - static final int VLEN; - static final long FULL_MASK; + public static final VectorSpecies SPECIES; + public static final int VLEN; + public static final long FULL_MASK; static { var s = ByteVector.SPECIES_PREFERRED;