Ability to add "jvm arguments" based on "@Param"

Vladimir Sitnikov sitnikov.vladimir at gmail.com
Fri Jun 5 10:31:28 UTC 2015


>Is it really *that* hard?

a) This raises a bar significantly. I can code that, no problem. The
problem is if you "just want bind operationsPerInvocation to a single
parameter", then you have to enumerate all the parameters.

If you just want having different flight recorder files (or garbage
collector log files), you have to write your own profiler.
Well, recording jfrs via ExternalProfiler is not that bad. However,
running tests with different concurrency levels is not "just adding an
option".

b) "progress bar" feature is broken when you run in separate Runners, isn't it?

>Suggest to extend the API to expose more knobs. (More
likely to fly)

What if ChainedOptionsBuilder#postConfigurator is added to
post-process the options?
It is just a proof of concept (no documentation, very little testing,
naming issues not solved, etc etc).

As far as I understand, even command line can support this API
extension via JSR 223 scripting API :)

.postConfigurator(new OptionsPostConfigurator() {
    @Override
    public void configure(PerBenchmarkOptionsBuilder builder) {
        builder.operationsPerInvocation(Integer.valueOf(builder.getParam("x")));
        // builder.threads(Integer.valueOf(builder.getParam("x"))), etc, etc
   }
})

The idea is to allow customization options that are part of
BenchmarkListEntry (excluding WorkloadParams, and, probably, some
others)

Do you feel it is an "intrusive change"?
As for me it looks like a straightforward change (modulo naming,
documentation and testing).

Vladimir
-------------- next part --------------
diff -r 13ffd1b8c9a1 jmh-core-it/src/test/java/org/openjdk/jmh/it/postprocessor/VariableOperationsPerInvocation.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core-it/src/test/java/org/openjdk/jmh/it/postprocessor/VariableOperationsPerInvocation.java	Fri Jun 05 12:20:30 2015 +0300
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.it.postprocessor;
+
+import junit.framework.Assert;
+import org.junit.Test;
+import org.openjdk.jmh.annotations.*;
+import org.openjdk.jmh.it.Fixtures;
+import org.openjdk.jmh.results.RunResult;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.*;
+
+import java.util.Collection;
+import java.util.concurrent.TimeUnit;
+
+ at Measurement(iterations = 1, time = 100, timeUnit = TimeUnit.MILLISECONDS)
+ at Warmup(iterations = 1, time = 100, timeUnit = TimeUnit.MILLISECONDS)
+ at Fork(1)
+ at State(Scope.Thread)
+public class VariableOperationsPerInvocation {
+    @Param({"1", "2", "3"})
+    public static int x;
+
+    @Benchmark
+    public void test() {
+        Fixtures.work();
+    }
+
+    @Test
+    public void full() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .postConfigurator(new OptionsPostConfigurator() {
+                    @Override
+                    public void configure(PerBenchmarkOptionsBuilder builder) {
+                        builder.operationsPerInvocation(Integer.valueOf(builder.getParam("x")));
+                    }
+                })
+                .build();
+
+        assertOperationsPerInvocation(new Runner(opts).run());
+    }
+
+    @Test
+    public void constrainedX() throws RunnerException {
+        Options opts = new OptionsBuilder()
+                .include(Fixtures.getTestMask(this.getClass()))
+                .shouldFailOnError(true)
+                .param("x", "2", "3")
+                .build();
+
+        assertOperationsPerInvocation(new Runner(opts).run());
+    }
+
+    private void assertOperationsPerInvocation(Collection<RunResult> run) {
+        for (RunResult result : run) {
+            int opsPerInvocation = result.getParams().getOpsPerInvocation();
+            String x = result.getParams().getParam("x");
+            Assert.assertEquals(x, Integer.toString(opsPerInvocation));
+        }
+    }
+}
diff -r 13ffd1b8c9a1 jmh-core/src/main/java/org/openjdk/jmh/runner/BenchmarkListEntry.java
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/BenchmarkListEntry.java	Thu May 21 18:45:31 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/BenchmarkListEntry.java	Fri Jun 05 12:20:30 2015 +0300
@@ -25,6 +25,7 @@
 package org.openjdk.jmh.runner;
 
 import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.runner.options.Options;
 import org.openjdk.jmh.runner.options.TimeValue;
 import org.openjdk.jmh.util.Optional;
 import org.openjdk.jmh.util.Utils;
@@ -170,6 +171,19 @@
         return br;
     }
 
+    public BenchmarkListEntry cloneWith(Options options) {
+        BenchmarkListEntry br = new BenchmarkListEntry(userName, generatedName, options.getBenchModes().iterator().next(),
+                options.getThreadGroups().get(), options.getThreads(),
+                options.getWarmupIterations(), options.getWarmupTime(), options.getWarmupBatchSize(),
+                options.getMeasurementIterations(), options.getMeasurementTime(), options.getMeasurementBatchSize(),
+                options.getForkCount(), options.getWarmupForkCount(),
+                options.getJvm(), options.getJvmArgs(), options.getJvmArgsPrepend(), options.getJvmArgsAppend(),
+                params, options.getTimeUnit(), options.getOperationsPerInvocation(),
+                options.getTimeout());
+        br.workloadParams = workloadParams;
+        return br;
+    }
+
     public WorkloadParams getWorkloadParams() {
         return workloadParams;
     }
diff -r 13ffd1b8c9a1 jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java	Thu May 21 18:45:31 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java	Fri Jun 05 12:20:30 2015 +0300
@@ -40,17 +40,8 @@
 import org.openjdk.jmh.runner.format.OutputFormat;
 import org.openjdk.jmh.runner.format.OutputFormatFactory;
 import org.openjdk.jmh.runner.link.BinaryLinkServer;
-import org.openjdk.jmh.runner.options.Options;
-import org.openjdk.jmh.runner.options.TimeValue;
-import org.openjdk.jmh.runner.options.VerboseMode;
-import org.openjdk.jmh.util.FileUtils;
-import org.openjdk.jmh.util.HashMultimap;
-import org.openjdk.jmh.util.InputStreamDrainer;
-import org.openjdk.jmh.util.Multimap;
-import org.openjdk.jmh.util.TreeMultimap;
-import org.openjdk.jmh.util.UnCloseablePrintStream;
-import org.openjdk.jmh.util.Utils;
-import org.openjdk.jmh.util.Version;
+import org.openjdk.jmh.runner.options.*;
+import org.openjdk.jmh.util.*;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -305,6 +296,19 @@
             benchmarks.addAll(newBenchmarks);
         }
 
+        Optional<OptionsPostConfigurator> postConfigurator = options.getPostConfigurator();
+        if (postConfigurator.hasValue()) {
+            OptionsPostConfigurator configurator = postConfigurator.get();
+            List<BenchmarkListEntry> newBenchmarks = new ArrayList<BenchmarkListEntry>(benchmarks.size());
+            for (BenchmarkListEntry br : benchmarks) {
+                PerBenchmarkOptionsBuilderImpl opts = PerBenchmarkOptionsBuilderImpl.of(br, options);
+                configurator.configure(opts);
+                newBenchmarks.add(br.cloneWith(opts));
+            }
+            benchmarks.clear();
+            benchmarks.addAll(newBenchmarks);
+        }
+
         Collection<RunResult> results = runBenchmarks(benchmarks);
 
         // If user requested the result file, write it out.
diff -r 13ffd1b8c9a1 jmh-core/src/main/java/org/openjdk/jmh/runner/options/ChainedOptionsBuilder.java
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/options/ChainedOptionsBuilder.java	Thu May 21 18:45:31 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/options/ChainedOptionsBuilder.java	Fri Jun 05 12:20:30 2015 +0300
@@ -305,4 +305,5 @@
      */
     ChainedOptionsBuilder timeout(TimeValue value);
 
+    ChainedOptionsBuilder postConfigurator(OptionsPostConfigurator value);
 }
diff -r 13ffd1b8c9a1 jmh-core/src/main/java/org/openjdk/jmh/runner/options/CommandLineOptions.java
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/options/CommandLineOptions.java	Thu May 21 18:45:31 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/options/CommandLineOptions.java	Fri Jun 05 12:20:30 2015 +0300
@@ -632,4 +632,9 @@
     public Optional<TimeValue> getTimeout() {
         return timeout;
     }
+
+    @Override
+    public Optional<OptionsPostConfigurator> getPostConfigurator() {
+        return Optional.none(); // TODO: Use JSR-223 to instantiate OptionsPostConfigurator from the command line?
+    }
 }
diff -r 13ffd1b8c9a1 jmh-core/src/main/java/org/openjdk/jmh/runner/options/Options.java
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/options/Options.java	Thu May 21 18:45:31 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/options/Options.java	Fri Jun 05 12:20:30 2015 +0300
@@ -245,4 +245,10 @@
      */
     Optional<TimeValue> getTimeout();
 
+    /**
+     * Configurator of additional options.
+     * @return configurator
+     */
+    Optional<OptionsPostConfigurator> getPostConfigurator();
+
 }
diff -r 13ffd1b8c9a1 jmh-core/src/main/java/org/openjdk/jmh/runner/options/OptionsBuilder.java
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/options/OptionsBuilder.java	Thu May 21 18:45:31 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/options/OptionsBuilder.java	Fri Jun 05 12:20:30 2015 +0300
@@ -699,4 +699,23 @@
 
     // ---------------------------------------------------------------------------
 
+    private Optional<OptionsPostConfigurator> postConfigurator = Optional.none();
+
+    @Override
+    public ChainedOptionsBuilder postConfigurator(OptionsPostConfigurator value) {
+        this.postConfigurator = Optional.of(value);
+        return this;
+    }
+
+    @Override
+    public Optional<OptionsPostConfigurator> getPostConfigurator() {
+        if (otherOptions != null) {
+            return postConfigurator.orAnother(otherOptions.getPostConfigurator());
+        } else {
+            return postConfigurator;
+        }
+    }
+
+    // ---------------------------------------------------------------------------
+
 }
diff -r 13ffd1b8c9a1 jmh-core/src/main/java/org/openjdk/jmh/runner/options/OptionsPostConfigurator.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/options/OptionsPostConfigurator.java	Fri Jun 05 12:20:30 2015 +0300
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.runner.options;
+
+public interface OptionsPostConfigurator {
+    /**
+     * Post configure given set of options for a particular benchmark.
+     * For instance, you can use argument that is customized based on the test parameters.
+     *
+     * <p>Note: not all the options can be overridden
+     * @param builder
+     */
+    void configure(PerBenchmarkOptionsBuilder builder);
+}
diff -r 13ffd1b8c9a1 jmh-core/src/main/java/org/openjdk/jmh/runner/options/PerBenchmarkOptionsBuilder.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/options/PerBenchmarkOptionsBuilder.java	Fri Jun 05 12:20:30 2015 +0300
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.runner.options;
+
+public interface PerBenchmarkOptionsBuilder extends ChainedOptionsBuilder, Options {
+    String getUsername();
+    String getGeneratedTarget();
+    String getParam(String param);
+}
diff -r 13ffd1b8c9a1 jmh-core/src/main/java/org/openjdk/jmh/runner/options/PerBenchmarkOptionsBuilderImpl.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/options/PerBenchmarkOptionsBuilderImpl.java	Fri Jun 05 12:20:30 2015 +0300
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.runner.options;
+
+import org.openjdk.jmh.runner.BenchmarkListEntry;
+import org.openjdk.jmh.runner.WorkloadParams;
+
+public class PerBenchmarkOptionsBuilderImpl extends OptionsBuilder implements PerBenchmarkOptionsBuilder {
+    private final BenchmarkListEntry br;
+
+    public PerBenchmarkOptionsBuilderImpl(BenchmarkListEntry br) {
+        this.br = br;
+    }
+
+    public static PerBenchmarkOptionsBuilderImpl of(BenchmarkListEntry br, Options parent) {
+        PerBenchmarkOptionsBuilderImpl out = new PerBenchmarkOptionsBuilderImpl(br);
+        out.parent(parent);
+        out.mode(br.getMode());
+        if (br.getThreads().hasValue()) out.threads(br.getThreads().get());
+        if (br.getJvmArgsAppend().hasValue()) out.jvmArgsAppend(br.getJvmArgsAppend().get().toArray(new String[0]));
+        out.threadGroups(br.getThreadGroups());
+        if (br.getWarmupIterations().hasValue()) out.warmupIterations(br.getWarmupIterations().get());
+        if (br.getWarmupBatchSize().hasValue()) out.warmupBatchSize(br.getWarmupBatchSize().get());
+        if (br.getWarmupTime().hasValue()) out.warmupTime(br.getWarmupTime().get());
+        if (br.getMeasurementIterations().hasValue()) out.measurementIterations(br.getMeasurementIterations().get());
+        if (br.getMeasurementBatchSize().hasValue()) out.measurementBatchSize(br.getMeasurementBatchSize().get());
+        if (br.getMeasurementTime().hasValue()) out.measurementTime(br.getMeasurementTime().get());
+        if (br.getForks().hasValue()) out.forks(br.getForks().get());
+        if (br.getWarmupForks().hasValue()) out.warmupForks(br.getWarmupForks().get());
+        if (br.getJvm().hasValue()) out.jvm(br.getJvm().get());
+        if (br.getJvmArgs().hasValue()) out.jvmArgs(br.getJvmArgs().get().toArray(new String[0]));
+        if (br.getJvmArgsAppend().hasValue()) out.jvmArgsAppend(br.getJvmArgsAppend().get().toArray(new String[0]));
+        if (br.getJvmArgsPrepend().hasValue()) out.jvmArgsPrepend(br.getJvmArgsPrepend().get().toArray(new String[0]));
+        if (br.getTimeUnit().hasValue()) out.timeUnit(br.getTimeUnit().get());
+        if (br.getOperationsPerInvocation().hasValue()) out.operationsPerInvocation(br.getOperationsPerInvocation().get());
+        WorkloadParams params = br.getWorkloadParams();
+        // Just in case, pretend we are running the exact param combination
+        for (String param : params.keys()) {
+            out.param(param, params.get(param));
+        }
+        return out;
+    }
+
+    public String getUsername() {
+        return br.getUsername();
+    }
+
+    public String getGeneratedTarget() {
+        return br.generatedTarget();
+    }
+
+    public String getParam(String param) {
+        return br.getWorkloadParams().get(param);
+    }
+}


More information about the jmh-dev mailing list