This is an automated email from the ASF dual-hosted git repository. xuanwo pushed a commit to branch xuanwo/java-get-perf in repository https://gitbox.apache.org/repos/asf/opendal.git
commit dcefe0eb5ceb7a5c1df1e6ea37f762dfc98d4af1 Author: Xuanwo <[email protected]> AuthorDate: Mon Dec 22 22:47:37 2025 +0800 Fix build --- bindings/java/pom.xml | 98 ++++++++++++++ bindings/java/src/executor.rs | 2 +- .../java/org/apache/opendal/bench/jmh/Main.java | 82 ++++++++++++ .../bench/jmh/OperatorReadJmhBenchmark.java | 147 +++++++++++++++++++++ 4 files changed, 328 insertions(+), 1 deletion(-) diff --git a/bindings/java/pom.xml b/bindings/java/pom.xml index cb8b71be7..10ed56d70 100644 --- a/bindings/java/pom.xml +++ b/bindings/java/pom.xml @@ -81,6 +81,9 @@ <palantir-java-format.version>2.36.0</palantir-java-format.version> <spotless.version>2.39.0</spotless.version> <maven-javadoc-plugin.version>3.6.3</maven-javadoc-plugin.version> + <build-helper-maven-plugin.version>3.6.0</build-helper-maven-plugin.version> + <maven-shade-plugin.version>3.6.1</maven-shade-plugin.version> + <jmh.version>1.37</jmh.version> </properties> <dependencyManagement> @@ -305,4 +308,99 @@ </plugin> </plugins> </build> + + <profiles> + <profile> + <id>jmh</id> + <dependencies> + <dependency> + <groupId>org.openjdk.jmh</groupId> + <artifactId>jmh-core</artifactId> + <version>${jmh.version}</version> + </dependency> + <dependency> + <groupId>org.openjdk.jmh</groupId> + <artifactId>jmh-generator-annprocess</artifactId> + <version>${jmh.version}</version> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <id>default-jar</id> + <configuration> + <excludes combine.self="override"/> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>${build-helper-maven-plugin.version}</version> + <executions> + <execution> + <id>add-jmh-source</id> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source>src/jmh/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>${maven-compiler-plugin.version}</version> + <configuration> + <annotationProcessorPaths> + <path> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>${lombok.version}</version> + </path> + <path> + <groupId>org.openjdk.jmh</groupId> + <artifactId>jmh-generator-annprocess</artifactId> + <version>${jmh.version}</version> + </path> + </annotationProcessorPaths> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>${maven-shade-plugin.version}</version> + <executions> + <execution> + <id>shade-jmh</id> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <finalName>benchmarks</finalName> + <createDependencyReducedPom>false</createDependencyReducedPom> + <transformers> + <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <mainClass>org.apache.opendal.bench.jmh.Main</mainClass> + </transformer> + </transformers> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> </project> diff --git a/bindings/java/src/executor.rs b/bindings/java/src/executor.rs index af0dd13f5..caa8263ae 100644 --- a/bindings/java/src/executor.rs +++ b/bindings/java/src/executor.rs @@ -129,7 +129,7 @@ pub(crate) fn make_tokio_executor(env: &mut JNIEnv, cores: usize) -> Result<Exec move || { ENV.with(|cell| { let mut env = vm - .attach_current_thread_permanently() + .attach_current_thread_as_daemon() .expect("attach thread must succeed"); set_current_thread_name(&mut env) diff --git a/bindings/java/src/jmh/java/org/apache/opendal/bench/jmh/Main.java b/bindings/java/src/jmh/java/org/apache/opendal/bench/jmh/Main.java new file mode 100644 index 000000000..3169c4bed --- /dev/null +++ b/bindings/java/src/jmh/java/org/apache/opendal/bench/jmh/Main.java @@ -0,0 +1,82 @@ +/* + * 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.opendal.bench.jmh; + +import java.util.Collection; +import org.openjdk.jmh.infra.BenchmarkParams; +import org.openjdk.jmh.results.RunResult; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.CommandLineOptions; +import org.openjdk.jmh.runner.options.ChainedOptionsBuilder; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.VerboseMode; + +public final class Main { + private static final double BYTES_PER_MIB = 1024.0 * 1024.0; + + private Main() {} + + public static void main(String[] args) throws Exception { + final CommandLineOptions cli = new CommandLineOptions(args); + + final ChainedOptionsBuilder builder = new OptionsBuilder().parent(cli); + if (!hasVerbosityArg(args)) { + builder.verbosity(VerboseMode.SILENT); + } + + final Options opt = builder.build(); + final Collection<RunResult> results = new Runner(opt).run(); + + System.out.println(); + System.out.println("Benchmark MiB/s"); + for (final RunResult rr : results) { + final double mibPerSec = toMibPerSec(rr); + System.out.printf("%-45s %10.3f%n", rr.getParams().getBenchmark(), mibPerSec); + } + } + + private static double toMibPerSec(RunResult rr) { + final BenchmarkParams params = rr.getParams(); + final String size = params.getParam("sizeBytes"); + if (size == null) { + throw new IllegalStateException("missing sizeBytes parameter for " + params.getBenchmark()); + } + final double mibPerOp = Long.parseLong(size) / BYTES_PER_MIB; + final double opsPerSec = rr.getPrimaryResult().getScore(); + return opsPerSec * mibPerOp; + } + + private static boolean hasVerbosityArg(String[] args) { + for (int i = 0; i < args.length; i++) { + final String a = args[i]; + if ("-v".equals(a) || "--verbosity".equals(a)) { + return true; + } + if (a.startsWith("-v") && a.length() > 2) { + return true; + } + if (a.startsWith("--verbosity=")) { + return true; + } + } + return false; + } +} diff --git a/bindings/java/src/jmh/java/org/apache/opendal/bench/jmh/OperatorReadJmhBenchmark.java b/bindings/java/src/jmh/java/org/apache/opendal/bench/jmh/OperatorReadJmhBenchmark.java new file mode 100644 index 000000000..68125e9b5 --- /dev/null +++ b/bindings/java/src/jmh/java/org/apache/opendal/bench/jmh/OperatorReadJmhBenchmark.java @@ -0,0 +1,147 @@ +/* + * 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.opendal.bench.jmh; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import org.apache.opendal.Operator; +import org.apache.opendal.OperatorInputStream; +import org.openjdk.jmh.annotations.AuxCounters; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 1, time = 10, timeUnit = TimeUnit.SECONDS) +@Fork(1) +@Threads(1) +public class OperatorReadJmhBenchmark { + private static final double BYTES_PER_MIB = 1024.0 * 1024.0; + + @AuxCounters(AuxCounters.Type.OPERATIONS) + @State(Scope.Thread) + public static class Counters { + public double mibPerSec; + + @Setup(Level.Iteration) + public void reset() { + mibPerSec = 0.0; + } + } + + @State(Scope.Thread) + public static class BenchmarkState { + @Param({"16777216"}) + public int sizeBytes; + + @Param({"8192"}) + public int bufferBytes; + + private Operator operator; + private String path; + private byte[] buf; + private OutputStream out; + + @Setup(Level.Trial) + public void setup() { + final Map<String, String> config = new HashMap<String, String>(); + this.operator = Operator.of("memory", config); + this.path = "bench.bin"; + + final byte[] content = new byte[sizeBytes]; + new Random(0).nextBytes(content); + this.operator.write(path, content); + + this.buf = new byte[bufferBytes]; + this.out = new NullOutputStream(); + } + + @TearDown(Level.Trial) + public void teardown() { + if (this.operator != null) { + this.operator.close(); + } + } + } + + @Benchmark + public void readRange(BenchmarkState state, Counters counters, Blackhole bh) throws IOException { + final byte[] bytes = state.operator.read(state.path, 0, state.sizeBytes); + if (bytes.length != state.sizeBytes) { + throw new IllegalStateException("short read: " + bytes.length + " != " + state.sizeBytes); + } + state.out.write(bytes); + counters.mibPerSec += bytes.length / BYTES_PER_MIB; + bh.consume(bytes.length); + } + + @Benchmark + public void createInputStream(BenchmarkState state, Counters counters, Blackhole bh) throws IOException { + long total = 0; + long acc = 0; + + try (final OperatorInputStream in = state.operator.createInputStream(state.path)) { + while (true) { + final int n = in.read(state.buf); + if (n < 0) { + break; + } + if (n == 0) { + continue; + } + state.out.write(state.buf, 0, n); + total += n; + acc ^= ((long) state.buf[0] << 32) ^ (state.buf[n - 1] & 0xFFL); + } + } + + if (total != state.sizeBytes) { + throw new IllegalStateException("short read: " + total + " != " + state.sizeBytes); + } + counters.mibPerSec += total / BYTES_PER_MIB; + bh.consume(acc); + } + + private static final class NullOutputStream extends OutputStream { + @Override + public void write(int b) {} + + @Override + public void write(byte[] b, int off, int len) {} + } +}
