Coder Social home page Coder Social logo

jmh-gradle-plugin's Introduction

JMH Gradle Plugin

Build Status Coverage Status (coveralls) Download Apache License 2

This plugin integrates the JMH micro-benchmarking framework with Gradle.

Usage

build.gradle
plugins {
  id "me.champeau.jmh" version "0.7.2"
}
Warning
Versions of the plugin prior to 0.6.0 used the me.champeau.gradle.jmh plugin id.

Samples can be found in the samples folder.

What plugin version to use?

Version 0.6+ requires Gradle 6.8+.

Gradle Minimal plugin version

8.0

0.7.0

7.0

0.5.3

5.5

0.5.0

5.1

0.4.8

4.9

0.4.7 (to benefit from lazy tasks API)

4.8

0.4.5

4.7

0.4.5

4.6

0.4.5

4.5

0.4.5

4.4

0.4.5

4.3

0.4.5

4.2

0.4.4

4.1

0.4.4

Configuration

The plugin makes it easy to integrate into an existing project thanks to a specific configuration. In particular, benchmark source files are expected to be found in the src/jmh directory:

src/jmh
     |- java       : java sources for benchmarks
     |- resources  : resources for benchmarks

The plugin creates a jmh configuration that you should use if your benchmark files depend on a 3rd party library. For example, if you want to use commons-io, you can add the dependency like this:

build.gradle
dependencies {
    jmh 'commons-io:commons-io:2.7'
}

The plugin uses JMH 1.37. You can upgrade the version just by changing the version in the dependencies block:

build.gradle
dependencies {
    jmh 'org.openjdk.jmh:jmh-core:0.9'
    jmh 'org.openjdk.jmh:jmh-generator-annprocess:0.9'
    jmh 'org.openjdk.jmh:jmh-generator-bytecode:0.9'
}

Tasks

The project will add several tasks:

  • jmhClasses : compiles raw benchmark code

  • jmhRunBytecodeGenerator : runs bytecode generator over raw benchmark code and generates actual benchmarks

  • jmhCompileGeneratedClasses : compiles generated benchmarks

  • jmhJar : builds the JMH jar containing the JMH runtime and your compiled benchmark classes

  • jmh : executes the benchmarks

The jmh task is the main task and depends on the others so it is in general sufficient to execute this task:

gradle jmh

Configuration options

By default, all benchmarks will be executed, and the results will be generated into $buildDir/reports/jmh. But you can change various options thanks to the jmh configuration block. All configurations variables apart from includes are unset, implying that they fall back to the default JMH values:

build.gradle
jmh {
   includes = ['some regular expression'] // include pattern (regular expression) for benchmarks to be executed
   excludes = ['some regular expression'] // exclude pattern (regular expression) for benchmarks to be executed
   iterations = 10 // Number of measurement iterations to do.
   benchmarkMode = ['thrpt','ss'] // Benchmark mode. Available modes are: [Throughput/thrpt, AverageTime/avgt, SampleTime/sample, SingleShotTime/ss, All/all]
   batchSize = 1 // Batch size: number of benchmark method calls per operation. (some benchmark modes can ignore this setting)
   fork = 2 // How many times to forks a single benchmark. Use 0 to disable forking altogether
   failOnError = false // Should JMH fail immediately if any benchmark had experienced the unrecoverable error?
   forceGC = false // Should JMH force GC between iterations?
   jvm = 'myjvm' // Custom JVM to use when forking.
   jvmArgs = ['Custom JVM args to use when forking.']
   jvmArgsAppend = ['Custom JVM args to use when forking (append these)']
   jvmArgsPrepend =[ 'Custom JVM args to use when forking (prepend these)']
   humanOutputFile = project.file("${project.buildDir}/reports/jmh/human.txt") // human-readable output file
   resultsFile = project.file("${project.buildDir}/reports/jmh/results.txt") // results file
   operationsPerInvocation = 10 // Operations per invocation.
   benchmarkParameters =  [:] // Benchmark parameters.
   profilers = [] // Use profilers to collect additional data. Supported profilers: [cl, comp, gc, stack, perf, perfnorm, perfasm, xperf, xperfasm, hs_cl, hs_comp, hs_gc, hs_rt, hs_thr, async]
   timeOnIteration = '1s' // Time to spend at each measurement iteration.
   resultFormat = 'CSV' // Result format type (one of CSV, JSON, NONE, SCSV, TEXT)
   synchronizeIterations = false // Synchronize iterations?
   threads = 4 // Number of worker threads to run with.
   threadGroups = [2,3,4] //Override thread group distribution for asymmetric benchmarks.
   jmhTimeout = '1s' // Timeout for benchmark iteration.
   timeUnit = 'ms' // Output time unit. Available time units are: [m, s, ms, us, ns].
   verbosity = 'NORMAL' // Verbosity mode. Available modes are: [SILENT, NORMAL, EXTRA]
   warmup = '1s' // Time to spend at each warmup iteration.
   warmupBatchSize = 10 // Warmup batch size: number of benchmark method calls per operation.
   warmupForks = 0 // How many warmup forks to make for a single benchmark. 0 to disable warmup forks.
   warmupIterations = 1 // Number of warmup iterations to do.
   warmupMode = 'INDI' // Warmup mode for warming up selected benchmarks. Warmup modes are: [INDI, BULK, BULK_INDI].
   warmupBenchmarks = ['.*Warmup'] // Warmup benchmarks to include in the run in addition to already selected. JMH will not measure these benchmarks, but only use them for the warmup.

   zip64 = true // Use ZIP64 format for bigger archives
   jmhVersion = '1.37' // Specifies JMH version
   includeTests = true // Allows to include test sources into generate JMH jar, i.e. use it when benchmarks depend on the test classes.
   duplicateClassesStrategy = DuplicatesStrategy.FAIL // Strategy to apply when encountring duplicate classes during creation of the fat jar (i.e. while executing jmhJar task)
}

JMH Options Mapping

The following table describes the mappings between JMH’s command line options and the plugin’s extension properties.

JMH Option Extension Property

-bm <mode>

benchmarkMode

-bs <int>

batchSize

-e <regexp+>

exclude

-f <int>

fork

-foe <bool>

failOnError

-gc <bool>

forceGC

-i <int>

iterations

-jvm <string>

jvm

-jvmArgs <string>

jvmArgs

-jvmArgsAppend <string>

jvmArgsAppend

-jvmArgsPrepend <string>

jvmArgsPrepend

-o <filename>

humanOutputFile

-opi <int>

operationsPerInvocation

-p <param={v,}*>

benchmarkParameters?

-prof <profiler>

profilers

-r <time>

timeOnIteration

-rf <type>

resultFormat

-rff <filename>

resultsFile

-si <bool>

synchronizeIterations

-t <int>

threads

-tg <int+>

threadGroups

-to <time>

jmhTimeout

-tu <TU>

timeUnit

-v <mode>

verbosity

-w <time>

warmup

-wbs <int>

warmupBatchSize

-wf <int>

warmupForks

-wi <int>

warmupIterations

-wm <mode>

warmupMode

-wmb <regexp+>

warmupBenchmarks

Dependency on project files

The jmh plugin makes it easy to test existing sources without having to create a separate project for this. This is the reason why you must put your benchmark source files into src/jmh/java instead of src/main/java. This means that by default, the jmh (benchmarks) task depends on your main (production) source set.

It is possible a dependency on the test source set by setting property includeTests to true inside jmh block.

Using JMH Gradle Plugin with Shadow Plugin

Optionally it is possible to use the Shadow Plugin (or the forked Shadow Plugin) to do actual JMH jar creation. The configuration of Shadow Plugin for JMH jar is done via jmhJar block.

For example:

build.gradle
jmhJar {
  append('META-INF/spring.handlers')
  append('META-INF/spring.schemas')
  exclude 'LICENSE'
}

Duplicate dependencies and classes

This plugin will merge all dependencies that are defined as part of jmh, runtime and optionally testRuntime configurations into a single set from which fat jar will be created when executing jmhJar task. This is done to ensure that no duplicate dependencies will be added the generated jar.

In addition plugin applies DuplicatesStrategy defined via duplicateClassesStrategy extension property to every class while creating fat jar. By default this property is set to DuplicatesStrategy.FAIL which means that upon detection of duplicate classes the task will fail.

It is possible to change this behavior by configuring duplicateClassesStrategy property via jmh block, e.g.:

build.gradle
jmh {
  duplicateClassesStrategy = DuplicatesStrategy.WARN
}

However if you do encounter problem with defaut value it means that the classpath or sources in your project do contain duplicate classes which means that it is not possible to predict which one will be used when fat jar will generated.

To deal with duplicate files other than classes use Shadow Plugin / forked Shadow Plugin capabilities, see Using JMH Gradle Plugin with Shadow Plugin.

jmh-gradle-plugin's People

Contributors

a10y avatar aalmiray avatar aegershman avatar bensteinert avatar big-guy avatar bric3 avatar chrisbrookes avatar donat avatar dtrunk90 avatar eatdrinksleepcode avatar eskatos avatar felldo avatar goooler avatar grv87 avatar gvsmirnov avatar ikhoon avatar imanushin avatar jake-at-work avatar joschi avatar jrhee17 avatar mariusvolkhart avatar melix avatar oehme avatar reinhapa avatar renovate[bot] avatar snazy avatar steppinrazor avatar sullis avatar surfing avatar vyazelenko avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jmh-gradle-plugin's Issues

Gradle jvmArgsAppend versus @Fork(.. jvmArgsAppend - over writings issue

Hi guys,

I;m profiling our App using the JMC (Java Mission Control) & Java Flight Recorder approach. The recorder needs some jvmArgsAppend -XX:+xx settings.

build.gradle :
jmh {
..
jvmArgsAppend ' -XX:+UnlockCommercialFeatures -XX:+FlightRecorder'
..
}

The concrete @fork() in the XXBenchmark.java needs some additional settings like :

BenchmarkIt.java
@benchmark
..
@fork(
value = 1,
jvmArgsAppend =
{
"-XX:StartFlightRecording=duration=60s,filename=./profiling-data.jfr,name=FULL,settings=profile",
"-XX:FlightRecorderOptions=settings=FULL.jfc,samplethreads=true"
})

@threads(..)
public void whenBenchmarkSomething() throws Exception {
...
}

Issue here : When I;m adding the jvmArgsAppend property in to the gradle jmh { jvmArgsAppend= } then it overwrites the @fork(jvmArgsAppend) settings. So for now I;m adding all jvmArgsAppend definition to the @fork annotation.

Thank you.

Cheers,
Jiri

PS :
XX:StartFlightRecording ==
https://github.com/biboudis/jmh-profilers

gradle jmh task fails with java.lang.IncompatibleClassChangeError

It appears that running the jmh task does not work.

Even with this simple benchmark:

package com.opendns.stats.domainmetadata.tools;

import java.io.IOException;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;

@State(Scope.Benchmark)
public class LoaderBenchmark {
    @Setup(Level.Trial)
    public static void setUp() throws IOException, InterruptedException {
    }

    @TearDown(Level.Trial)
    public void tearDown() throws InterruptedException {
    }

    @Benchmark
    @BenchmarkMode(Mode.AverageTime)
    public void newDomains1000() {
    }
}

This happens when I run the jmh task:

$ gradle jmh -i
Connected to daemon DaemonInfo{pid=28477, address=[5e63e4cf-5eb2-4c84-a149-7047c8e53f4a port:42336, addresses:[/0:0:0:0:0:0:0:1%lo, /127.0.0.1]], idle=true, context=DefaultDaemonContext[uid=2c1d894e-d9dd-40a4-baca-2be856bc48c8,javaHome=/usr/lib/jvm/java-8-openjdk,daemonRegistryDir=/home/dead10ck/.gradle/daemon,pid=28477,idleTimeout=10800000,daemonOpts=-XX:MaxPermSize=256m,-XX:+HeapDumpOnOutOfMemoryError,-Xmx1024m,-Dfile.encoding=UTF-8,-Duser.country=US,-Duser.language=en,-Duser.variant]}. Dispatching request Build{id=75ffc88c-c28f-417b-b234-8f8c27cec71d.1, currentDir=/home/dead10ck/src/inteldb/loader}.
Received result org.gradle.launcher.daemon.protocol.BuildStarted@5890e879 from daemon DaemonInfo{pid=28477, address=[5e63e4cf-5eb2-4c84-a149-7047c8e53f4a port:42336, addresses:[/0:0:0:0:0:0:0:1%lo, /127.0.0.1]], idle=true, context=DefaultDaemonContext[uid=2c1d894e-d9dd-40a4-baca-2be856bc48c8,javaHome=/usr/lib/jvm/java-8-openjdk,daemonRegistryDir=/home/dead10ck/.gradle/daemon,pid=28477,idleTimeout=10800000,daemonOpts=-XX:MaxPermSize=256m,-XX:+HeapDumpOnOutOfMemoryError,-Xmx1024m,-Dfile.encoding=UTF-8,-Duser.country=US,-Duser.language=en,-Duser.variant]} (build should be starting).
The client will now receive all logging from the daemon (pid: 28477). The daemon log file: /home/dead10ck/.gradle/daemon/2.11/daemon-28477.out.log
Starting 22nd build in daemon [uptime: 1 hrs 55 mins 38.618 secs, performance: 97%, memory: 47% of 954.7 MB]
Executing build with daemon context: DefaultDaemonContext[uid=2c1d894e-d9dd-40a4-baca-2be856bc48c8,javaHome=/usr/lib/jvm/java-8-openjdk,daemonRegistryDir=/home/dead10ck/.gradle/daemon,pid=28477,idleTimeout=10800000,daemonOpts=-XX:MaxPermSize=256m,-XX:+HeapDumpOnOutOfMemoryError,-Xmx1024m,-Dfile.encoding=UTF-8,-Duser.country=US,-Duser.language=en,-Duser.variant]
Starting Build
Settings evaluated using settings file '/home/dead10ck/src/inteldb/loader/settings.gradle'.
Projects loaded. Root project using build file '/home/dead10ck/src/inteldb/loader/build.gradle'.
Included projects: [root project 'inteldb-loader']
Evaluating root project 'inteldb-loader' using build file '/home/dead10ck/src/inteldb/loader/build.gradle'.
All projects evaluated.
Selected primary task 'jmh' from project :
Tasks to be executed: [task ':compileJava', task ':processResources', task ':classes', task ':compileJmhJava', task ':processJmhResources', task ':jmhClasses', task ':jmhJar', task ':jmh']
In-memory cache of /home/dead10ck/src/inteldb/loader/.gradle/2.11/taskArtifacts/fileHashes.bin: Size{601}, CacheStats{hitCount=16108, missCount=1028, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
In-memory cache of /home/dead10ck/src/inteldb/loader/.gradle/2.11/taskArtifacts/outputFileStates.bin: Size{14}, CacheStats{hitCount=167, missCount=14, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
In-memory cache of /home/dead10ck/src/inteldb/loader/.gradle/2.11/taskArtifacts/fileSnapshots.bin: Size{141}, CacheStats{hitCount=0, missCount=21, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
In-memory cache of /home/dead10ck/src/inteldb/loader/.gradle/2.11/taskArtifacts/taskArtifacts.bin: Size{11}, CacheStats{hitCount=84, missCount=14, loadSuccessCount=0, loadExceptionCount=0, totalLoadTime=0, evictionCount=0}
:compileJava (Thread[Daemon worker Thread 14,5,main]) started.
:compileJava
Skipping task ':compileJava' as it is up-to-date (took 0.638 secs).
:compileJava UP-TO-DATE
:compileJava (Thread[Daemon worker Thread 14,5,main]) completed. Took 0.64 secs.
:processResources (Thread[Daemon worker Thread 14,5,main]) started.
:processResources
Skipping task ':processResources' as it is up-to-date (took 0.0 secs).
:processResources UP-TO-DATE
:processResources (Thread[Daemon worker Thread 14,5,main]) completed. Took 0.001 secs.
:classes (Thread[Daemon worker Thread 14,5,main]) started.
:classes
Skipping task ':classes' as it has no actions.
:classes UP-TO-DATE
:classes (Thread[Daemon worker Thread 14,5,main]) completed. Took 0.0 secs.
:compileJmhJava (Thread[Daemon worker Thread 14,5,main]) started.
:compileJmhJava
Skipping task ':compileJmhJava' as it is up-to-date (took 0.027 secs).
:compileJmhJava UP-TO-DATE
:compileJmhJava (Thread[Daemon worker Thread 14,5,main]) completed. Took 0.028 secs.
:processJmhResources (Thread[Daemon worker Thread 14,5,main]) started.
:processJmhResources
Skipping task ':processJmhResources' as it is up-to-date (took 0.0 secs).
:processJmhResources UP-TO-DATE
:processJmhResources (Thread[Daemon worker Thread 14,5,main]) completed. Took 0.001 secs.
:jmhClasses (Thread[Daemon worker Thread 14,5,main]) started.
:jmhClasses
Skipping task ':jmhClasses' as it has no actions.
:jmhClasses UP-TO-DATE
:jmhClasses (Thread[Daemon worker Thread 14,5,main]) completed. Took 0.0 secs.
:jmhJar (Thread[Daemon worker Thread 14,5,main]) started.
:jmhJar
Skipping task ':jmhJar' as it is up-to-date (took 0.001 secs).
:jmhJar UP-TO-DATE
:jmhJar (Thread[Daemon worker Thread 14,5,main]) completed. Took 0.002 secs.
:jmh (Thread[Daemon worker Thread 14,5,main]) started.
:jmh
Executing task ':jmh' (up-to-date check took 0.0 secs) due to:
  Task has not declared any outputs.
Starting process 'command '/usr/lib/jvm/java-8-openjdk/bin/java''. Working directory: /home/dead10ck/src/inteldb/loader Command: /usr/lib/jvm/java-8-openjdk/bin/java -Dfile.encoding=UTF-8 -Duser.country=US -Duser.language=en -Duser.variant -cp /home/dead10ck/src/inteldb/loader/build/libs/inteldb-loader-0.76-SNAPSHOT-jmh.jar org.openjdk.jmh.Main LoaderBenchmark -i 5 -f 5 -o build/reports/jmh/human.txt -rff build/reports/jmh/results.txt -wi 5
Successfully started process 'command '/usr/lib/jvm/java-8-openjdk/bin/java''
Exception in thread "main" java.lang.IncompatibleClassChangeError: Implementing class
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at org.openjdk.jmh.Main.main(Main.java:44)
:jmh FAILED
:jmh (Thread[Daemon worker Thread 14,5,main]) completed. Took 0.074 secs.

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':jmh'.
> Process 'command '/usr/lib/jvm/java-8-openjdk/bin/java'' finished with non-zero exit value 1

* Try:
Run with --stacktrace option to get the stack trace. Run with --debug option to get more log output.

BUILD FAILED

Total time: 1.356 secs
Stopped 0 compiler daemon(s).
Received result Failure[value=org.gradle.initialization.ReportedException: org.gradle.internal.exceptions.LocationAwareException: Execution failed for task ':jmh'.] from daemon DaemonInfo{pid=28477, address=[5e63e4cf-5eb2-4c84-a149-7047c8e53f4a port:42336, addresses:[/0:0:0:0:0:0:0:1%lo, /127.0.0.1]], idle=true, context=DefaultDaemonContext[uid=2c1d894e-d9dd-40a4-baca-2be856bc48c8,javaHome=/usr/lib/jvm/java-8-openjdk,daemonRegistryDir=/home/dead10ck/.gradle/daemon,pid=28477,idleTimeout=10800000,daemonOpts=-XX:MaxPermSize=256m,-XX:+HeapDumpOnOutOfMemoryError,-Xmx1024m,-Dfile.encoding=UTF-8,-Duser.country=US,-Duser.language=en,-Duser.variant]} (build should be done).

I am running Gradle v2.11, Java 1.8.0_74

Provide support for Gradle 2.0

Currently running "jmh" task results in exception:

Ambiguous method overloading for method me.champeau.gradle.JMHPluginExtension_Decorated#addOption.
  Cannot resolve which method to invoke for [class java.util.ArrayList, null, class java.lang.String] due to overlapping prototypes between:
    [interface java.util.List, interface java.util.List, class java.lang.String]
    [interface java.util.List, interface java.util.Map, class java.lang.String]

Unable to find the resource: /META-INF/BenchmarkList

Exception in thread "main" java.lang.RuntimeException: ERROR: Unable to find the resource: /META-INF/BenchmarkList
    at org.openjdk.jmh.runner.AbstractResourceReader.getReaders(AbstractResourceReader.java:97)
    at org.openjdk.jmh.runner.BenchmarkList.find(BenchmarkList.java:104)
    at org.openjdk.jmh.runner.Runner.internalRun(Runner.java:242)
    at org.openjdk.jmh.runner.Runner.run(Runner.java:196)

Problem running benchmarks from IntelliJ

Hello,

I'm using the plugin in combination with IntelliJ IDEA 15.0.4 and I try to run the example JMHSample_25_API_GA, which uses the JMH API.

But when I try to execute the main() method I always get the following error:

Exception in thread "main" java.lang.RuntimeException: ERROR: Unable to find the resource: /META-INF/BenchmarkList

My build.gradle file:

plugins {
    id "me.champeau.gradle.jmh" version "0.3.0"
}

apply plugin: 'java'
apply plugin: 'idea'

group 'jmhTest'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8


repositories {
    mavenCentral()
}

apply plugin: 'me.champeau.gradle.jmh'

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.11'
}

Running the task jmhJar, creates all the needed classes and also the BenchmarkList, in the directory jmh-generated-classes/META-INF

Maybe someone can help me with my problem, best regards
Peter

Add option to report progress

The plugin always passes the human readable output file to jmh. This unfortunately results in JMH not displaying any output onto the console/stdout. This can become quite annoying if running from within an IDE (or trying to follow progress on a CI server too).

Would it be possible to add an option to multiplex the output to STDOUt too?

Cannot handle archivesBaseName and version build options

As reported in #13, the plugin fails if the archivesBaseName or version build options are specified. This plugin essentially adds the raw jar location (build/libs/<project-name>-jmh.jar) when executing the jmh task, even if that is not the one built because of modified options.

When do you expect to have the next release?

Hi,

I noticed the last release of this jmh plugin was over a year ago, and since then there were lots of enhancements/bug fixes, is there plan to release a new version soon?

Thanks
Baohua

Using jmh gradle plugin with eclipse

Has anyone successfully used the plugin with eclipse? When I try to open my gradle project which uses the jmh plugin, eclipse does not add the jmh jar as a dependency. So, eclipse complains about all the jmh annotations on my benchmark classes. This all works correctly in Idea. So, this is an eclipse issue and not a plugin issue. I'm looking for any advice on making this work with eclipse. Ofcourse, I could add the jmh dependency explicitly but before I do that I want to be sure there is better solution. I've colleagues who use Eclipse, so I'm trying to make sure it works in eclipse correctly.

I'm using the latest Eclipse 4.5 with the latest Buildship Gradle plugin for eclipse.

Error: Could not find or load main class org.openjdk.jmh.Main

The relevant parts of the build.gradle file:

buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'me.champeau.gradle:jmh-gradle-plugin:0.1.2'
    }
}

apply plugin: 'java'
apply plugin: 'me.champeau.gradle.jmh'

jmh {
    include = ".*Benchmark.java"
}

The classpath problem:

$ gradle jmh             
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileJmhJava UP-TO-DATE
:processJmhResources UP-TO-DATE
:jmhClasses UP-TO-DATE
:jmhJar UP-TO-DATE
:jmh
Error: Could not find or load main class org.openjdk.jmh.Main
:jmh FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':jmh'.
> Process 'command '/usr/lib/jvm/java-8-oracle/bin/java'' finished with non-zero exit value 1

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

If there's any other information you need to reproduce this, let me know.

Support other languages

The reason for #28 was that I was using Scala to write the benchmark. It would be nice to have support for the other JVM languages, just like JMH itself.

Progress Reporting

It would be great if the plugin reported progress of the JMH benchmark that is executing. In the current form it is stuck at 88% and appears like the build is broken to a newbie.

No benchmarks are executed by default

By default jmh task will not find any benchmarks to execute.

For example here is gist that contains gradle.build and SampleBenchmark.java. If I try to execute gradle jmh I get the following output:

:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileJmhJavawarning: Supported source version 'RELEASE_6' from annotation processor 'org.openjdk.jmh.generators.BenchmarkProcessor' less than -source '1.7'
1 warning

:processJmhResources UP-TO-DATE
:jmhClasses
:jmhJar
:jmhNo matching benchmarks. Miss-spelled regexp?
Use EXTRA verbose mode to debug the pattern matching.


BUILD SUCCESSFUL

Total time: 8.193 secs

However when I do gradle jmhJar followed by command line invocation (i.e. java -jar build\libs\jmh-test-jmh.jar) then execution is started:

P:\Projects\OpenSource\jmh-test>java -jar build\libs\jmh-test-jmh.jar
# VM invoker: C:\Program Files\Java\jre7\bin\java.exe
# VM options: <none>
# Warmup: 5 iterations, 1 s each
# Measurement: 5 iterations, 1 s each
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: test.SampleBenchmark.baseLine

# Run progress: 0.00% complete, ETA 00:00:10
# Fork: 1 of 1
# Warmup Iteration   1: 1.115 ns/op
# Warmup Iteration   2: 1.122 ns/op
# Warmup Iteration   3: 1.107 ns/op
# Warmup Iteration   4: 1.108 ns/op
# Warmup Iteration   5: 1.131 ns/op
Iteration   1: 1.120 ns/op
Iteration   2: 1.321 ns/op
Iteration   3: 1.207 ns/op
Iteration   4: 1.107 ns/op
Iteration   5: 1.105 ns/op


Result: 1.172 ▒(99.9%) 0.360 ns/op [Average]
  Statistics: (min, avg, max) = (1.105, 1.172, 1.321), stdev = 0.093
  Confidence interval (99.9%): [0.812, 1.532]


# Run complete. Total time: 00:00:12

Benchmark                     Mode  Samples  Score  Score error  Units
t.SampleBenchmark.baseLine    avgt        5  1.172        0.360  ns/op

It seems that default include is not correct, i.e. at least it's not the same as executing JAR directly.

jmhJar does not rebuild JAR if bechmarks are changed

Attempt to execute jmh or jmhJar after changing benchmark does not result in destination JAR being re-build.
Here is output from running gradle jmhJar:

:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileJmhJavawarning: Supported source version 'RELEASE_6' from annotation processor 'org.openjdk.jmh.generators.BenchmarkProcessor' less than -source '1.7'
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 warning

:processJmhResources UP-TO-DATE
:jmhClasses
:jmhJar UP-TO-DATE

BUILD SUCCESSFUL

The problem is that after classes are recompiled the jmhJar reports that everything is up to date.

append method not found for jmhJar

Shadow Plugin doesn't work

jmhJar {
    append 'META-INF/services/com.hazelcast.client.ClientExtension'
}
$ ./gradlew jmhJar

FAILURE: Build failed with an exception.

* Where:
Build file '/Users/apple/projects/build.gradle' line: 119

* What went wrong:
A problem occurred evaluating root project 'Benchmark Suite'.
> Could not find method append() for arguments [META-INF/services/com.hazelcast.client.ClientExtension] on root project 'Benchmark Suite'.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 3.302 secs

java.lang.SecurityException: Invalid signature file digest for Manifest main attributes

java.lang.SecurityException is thrown on attempt to execute jmh task if one of the dependencies is a signed jar:

java.lang.SecurityException: Invalid signature file digest for Manifest main
        at sun.security.util.SignatureFileVerifier.processImpl(SignatureFileVeri
        at sun.security.util.SignatureFileVerifier.process(SignatureFileVerifier
        at java.util.jar.JarVerifier.processEntry(JarVerifier.java:274)
        at java.util.jar.JarVerifier.update(JarVerifier.java:228)
        at java.util.jar.JarFile.initializeVerifier(JarFile.java:348)
        at java.util.jar.JarFile.getInputStream(JarFile.java:415)
        at sun.misc.URLClassPath$JarLoader$2.getInputStream(URLClassPath.java:77
        at sun.misc.Resource.cachedInputStream(Resource.java:77)
        at sun.misc.Resource.getByteBuffer(Resource.java:160)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:436)
        at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
        at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:482)
Exception in thread "main"  FAILED

To reproduce use sample project jmh-gradle-plugin-signed-jars-example.

Basically the problem is that when uber-jar is created it picks up signature files from the META-INF of the corresponding jar.
To fix this issue one needs to exclude signature files as desribed in http://zhentao-li.blogspot.ch/2012/06/maven-shade-plugin-invalid-signature.html and http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar.

Issue with benchmarkMode?

When I use

  benchmarkMode = ['thrpt','ss']

I get

Exception in thread "main" java.lang.IllegalStateException: Unable to parse benchmark mode: "[thrpt"
Known values are [Throughput/thrpt, AverageTime/avgt, SampleTime/sample, SingleShotTime/ss, All/all]
    at org.openjdk.jmh.annotations.Mode.deepValueOf(Mode.java:124)
    at org.openjdk.jmh.runner.options.CommandLineOptions.<init>(CommandLineOptions.java:314)
    at org.openjdk.jmh.Main.main(Main.java:41)

jmh-gradle-plugin does not work properly with idea module

The src/jmh/java is not set up in the test sources, and the associated configuration is not linked up with it.

I also found that the built-in dependencies ended up doubled unless I first removed them. Workaround to get the dependencies all on latest versions looks like this:

configurations.jmh.dependencies.clear()
dependencies {
    jmh 'junit:junit:4.11',
            'org.openjdk.jmh:jmh-generator-annprocess:1.1.1',
            'net.sf.jopt-simple:jopt-simple:4.7',
            'org.apache.commons:commons-math3:3.3',
            'nf.fr.eraasoft:objectpool:1.1.2',
            'org.apache.commons:commons-pool2:2.2'
}
idea {
    module {
        testSourceDirs += file('src/jmh/java')
        scopes.TEST.plus += [ configurations.jmh ]
    }
}

Shadow plugin detection is broken when using new plugin DSL

If I include this plugin the old way:

buildscript {
  repositories {
    jcenter()
  }

  dependencies {
    classpath "me.champeau.gradle:jmh-gradle-plugin:0.2.0"
  }
}
...
apply plugin: 'me.champeau.gradle.jmh'

then jmhJar is built correctly, and includes services entries.

If I include it the new way:

plugins {
  id 'me.champeau.gradle.jmh' version '0.2.0'
}

then jmhJar is built as just a jar that excludes services entries, which breaks my app. (On a related note, why are you excluding services files when assembling a jar without the shadow plugin?)

I tried to investigate a little (I theorize that perhaps classloader isolation with the new DSL is the cause here) but I couldn't get far since I couldn't figure out how to depend on a local snapshot with the plugin DSL. I did notice that PluginContainer claims to no longer be the preferred way to look for plugins, so perhaps in JMHPlugin:58 you want something like this:

        if (!project.plugins.pluginManager.hasPlugin('com.github.johnrengelman.shadow')) {

That's just a theory based on my assumption that the recommended way to do it knows how to cross the DSL's plugin boundaries.

Eclipse integration

The Eclipse Gradle plugin does not include custom sourceSets as source folders. The following snippet resolves that issue and could be performed by the jmh plugin if the eclipse plugin is detected.

apply plugin: 'eclipse'

plugins.withType(EclipsePlugin) {
  project.eclipse.classpath.plusConfigurations += [ configurations.jmh ]
}

Runtime classpath from the jmh sourceset should be taken into account when running tests

If I declare something like:

sourceSets {
    jmh {
        java {
            compileClasspath += project(':shared').sourceSets.main.output
            runtimeClasspath += project(':shared').sourceSets.main.output
        }
    }
}

compile class-path will be correctly taken into account when compiling the files from the jmh sourceset. However, the runtime class-path will not be considered when running tests, thus tests end up with ClassNotFoundException.

The workout that works for me is:

dependencies {
    jmh project(':shared')
}

but I would still expect that the runtimeClasspath is taken into account.

jmh.jmhVersion definition does not work properly

Hi,
I'm currently working with the jmh-gradle plugin. As JMH 1.3.3 was released few days back I tried to use the jmhVersion property which was introduced by a former pull request. The implementation actually does not work, because the property gets set after the dependencies have been added to the configuration. I implemented the dependency definition in a beforeResolve action and for that upgraded to gradle 2.2.1. Please find the fix in my fork. I will provide a pull request the next days to get it merged back to the main project. I first would like to write some additional tests for that.

JMH jars not visible when importing to IntelliJ

I had a couple issues using this plugin in an IDEA project. I'm not using Gradle's IDEA Plugin, and would prefer to stay with IntelliJ's default Gradle handling.

The issues were:

  • JMH jars not visible in the IDE - which made writing benchmarks quite challenging,
  • once I added both JMH jars to compile configuration, I couldn't properly execute the jmh task any more. I kept getting No matching benchmarks. Miss-spelled regexp?.

I fixed these issues in my local branch somehow. Would you happen to be interested in incorporating them into your plugin?

duplicateClassesStrategy options

Dear Cedric,

Let me start that there is nothing I can do about organizations having same classes in different libs.

So, I am only considering 3 other options:
include
exclude
warn

However, I would like to make sure to use only an option that complies with classpath rule: only first occurrence counts.

Based on the description in DuplicatesStrategy JavaDoc, it seems that only exclude does. However, I like a warn ability to report about duplicates.

Can you please confirm which one of the 3 complies with classpath rule?

Thank you

Benchmarks that rely on ServiceLoader do not work.

I want to write a Benchmark which uses another project that uses the ServiceLoader which in order to work requires a file in the classpath at the location META-INF/services/some.interface.name.
The problem is now that this plugin does not include the META-INF/services content into the benchmark-jar, see the JMHPlugin.groovy commit in changeset fc24778:

from(project.configurations.jmh.collect { it.isDirectory() ? it : project.zipTree(it) }) {
                    exclude '**/META-INF/services/**'
                }

What is the reason to exclude this content and is there a workaround to benchmark ServiceLoader based code?

Help is appreciated.

Build fails if sub project applies idea plugin but root project does not.

Perhaps we have an unusual setup but our root project does not apply the idea plugin but it is applied to our sub projects.
The following exception is thrown when running any gradle task.


Caused by: org.gradle.api.internal.MissingMethodException: Could not find method idea() for arguments [me.champeau.gradle.JMHPlugin$_apply_closure5_closure15@16d98aed] on root project 'my_project'.
    at org.gradle.api.internal.AbstractDynamicObject.methodMissingException(AbstractDynamicObject.java:68)
    at org.gradle.api.internal.AbstractDynamicObject.invokeMethod(AbstractDynamicObject.java:56)
    at org.gradle.api.internal.CompositeDynamicObject.invokeMethod(CompositeDynamicObject.java:172)
    at org.gradle.api.internal.project.DefaultProject_Decorated.invokeMethod(Unknown Source)
    at me.champeau.gradle.JMHPlugin$_apply_closure5.doCall(JMHPlugin.groovy:96)
    at org.gradle.listener.ClosureBackedMethodInvocationDispatch.dispatch(ClosureBackedMethodInvocationDispatch.java:40)
    at org.gradle.listener.ClosureBackedMethodInvocationDispatch.dispatch(ClosureBackedMethodInvocationDispatch.java:25)
    at org.gradle.listener.BroadcastDispatch.dispatch(BroadcastDispatch.java:83)
    at org.gradle.listener.BroadcastDispatch.dispatch(BroadcastDispatch.java:31)
    at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
    at com.sun.proxy.$Proxy12.afterEvaluate(Unknown Source)
    at org.gradle.configuration.project.LifecycleProjectEvaluator.notifyAfterEvaluate(LifecycleProjectEvaluator.java:79)
    ... 38 more

Bug with passing multiple parameters to jmh

If I need to pass multiple parameters to jmh with benchmarkParameters = [:], for example:

benchmarkParameters = [key1:value1, key2:value2, key3:value3]

The command line argument passed to the jmh main function looks like:

-p key1=value1,key2=value2,key3=value3

And the jmh main function is only able to get the first parameter listed.

I am using jmh 1.11, base on the source code of org.openjdk.jmh.runner.options.CommandLineOptions, the -p option is expected to be used once per parameter:

    OptionSpec<String> optParams = parser.accepts("p", "Benchmark parameters. This option is expected to be used " +
            "once per parameter. Parameter name and parameter values should be separated with equals sign. " +
            "Parameter values should be separated with commas.")
            .withRequiredArg().ofType(String.class).describedAs("param={v,}*");

So to be able to pass multiple parameters to jmh the command line arguments to jmh main function should actually be:

-p key1=value1 -p key2=value2 -p key3=value3

Comma is used to pass multiple values to a single parameter, and a new jmh benchmark will be executed for each value.

Would be great to fix this issue.

resultFile should use format's extension

The result file is defaulted to be named result.txt, regardless of the file format. I would have expected that a JSON format would be written to result.json.

Spring-based projects fail at runtime

If a project has a dependency on Spring framework then attempt to execute benchmarks will fail at runtime with org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/context].

To reproduce:

The problem is that generated JAR:

Contains duplicate files:

image

Does not merge spring.handlers and spring.schemas files:

image

This problem is a well known in Maven world. For example see MASSEMBLY-360 and maven-shade-plugin:Merging Content of Specific Files with AppendingTransformer and XmlAppendingTransformer.

Support running benchmarks without creating benchmark jar?

I'm trying to benchmark a class from my project. My project is part of an IntelliJ plugin, so it needs the whole IntelliJ framework on the classpath even though it needs only a couple of classes from it. When trying to run my benchmark, I get:

Execution failed for task ':jmhJar'.
archive contains more than 65535 entries.

I guess this is because it's trying to put the whole IntelliJ framework into the benchmark jar. Is it possible to run JMH via this plugin without creating the jar?

Doesn't work with IntelliJ IDEA 15

IntelliJ IDEA 14 could import gradle project with this plugin, with some problems: it doesn't understand jmhVersion (puts dependency to 1.3.2, not configured one) and hase source / dependency scope mismatch (easily fixable by hands).

But IDEA 15 could not properly import such project at all: it doesn't see any JMH-related dependencies.

'jmh' task does not include tests folder

Jmh task does not include test folder sources and resources, it makes benchmarks dependent on test classes fail to compile. Could you please let me to contribute the fix?

Command Line Options

The list of options to JMH needs to be updated to reflect JMH advances.

A better option might be to have a JMH options parameter that can combine a number of options.

ByteCodeGenerator or annotation processor?

Hi,

I am using jmh-gradle-plugin 0.3.0. Base on the src code, this plugin is using
org.openjdk.jmh.generators.bytecode.JmhBytecodeGenerator.

But when I tried to remove "jmh spec.external.jmhGeneratorAnnprocess" from my dependencies no META-INF/BenchmarkList is generated.

Is jmh-gradle-plugin still relying on the annoation process to generate META-INF/BenchmarkList? If so what's the purpose of running JmhBytecodeGenerator in task jmhRunBytecodeGenerator?

0.3.0 on bintray or mavencentral?

I love this plugin.

Is it possible to make it so releases are uploaded to bintray and mavencentral as well? Currently latest is only 0.2.0

Clarify classpath dependency

Hi, I work on grpc and we are using this gradle plugin for benchmarks. It seems there is a discrepancy between the example on the README:

  dependencies {
    classpath "me.champeau.gradle:jmh-gradle-plugin:0.3.0"
  }

and the gradle page: https://plugins.gradle.org/plugin/me.champeau.gradle.jmh

  dependencies {
    classpath "gradle.plugin.me.champeau.gradle:jmh-gradle-plugin:0.3.0"
  }

Which one is correct, and should they be made the same?

RFE: Command line parameters for jmh task

Currently the only way to configure benchmarks execution is via jmh block. However this configuration is static, i.e. any change requires changing build.gradle file. This is annoying especially if you want to change JVM params or number of threads.

It would be great to be able to supply command line args when invoking gradle jmh, e.g.:

gradle jmh -PjmhArgs="-t 4 -wi 5 -i 10"

Probably makes sense to wait for single argument option planned as part of #46.

Unable to find the resource: /META-INF/BenchmarkList

I am trying to run JMH against LoganSquare, which depends on the gradle apt plugin and also does annotation processing.
But every time I do and start the program using my entry point and the uberjar, i get the following exception message:

Unable to find the resource: /META-INF/BenchmarkList

My guess is the annotation processor or logansquare passes after the JMH plugin and wipes the file out.
Is there anything I can do?

Exception in jmhJar when using test sourceSet

I'm using version 0.3.0 of jmh-gradle-plugin in a Gradle 2.13 build.

I've encountered a problem where the build fails unless I have a src/test/resources folder in existence and containing at least one file. For the moment I've created a src/test/resources folder and populated with it a dummy text file. It would be nice not to have this workaround though.

I've had a quick look at the source but am lost in the world of Gradle code. To be honest I'm not even sure if what I'm trying to do is valid in the world of Gradle or if it is a bug in Gradle and not the JMH plugin.

I've created a simple project here which demonstrates the issue I am facing:

https://github.com/mipmapman/bar

Note that it occurs because I have the following:

dependencies {
    jmh sourceSets.test.output
}

I need this because my benchmark code extends/makes use of my test code. I am using this because even if I use the following in the JMH config without the above dependency line, I can't get past the compilation stage:

includeTests = true

When I run:

gradle clean jmhJar

I get the exception listed below.

As mentioned, to workaround this if I add a src/test/resources folder with a dummy file then the build works fine...

I'm struggling to see how I can specify to just use the compiled test classes and not expect test resources when stating:

dependencies {
    jmh sourceSets.test.output
}

:clean
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:compileJmhJava
:processJmhResources UP-TO-DATE
:jmhClasses
:jmhRunBytecodeGenerator
Processing 1 classes from /Users/nick/Documents/Personal/projects/tsl8r/bar/build/classes/jmh with "reflection" generator
Writing out Java source to /Users/nick/Documents/Personal/projects/tsl8r/bar/build/jmh-generated-sources and resources to /Users/nick/Documents/Personal/projects/tsl8r/bar/build/jmh-generated-classes
:jmhCompileGeneratedClasses
:jmhJar FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':jmhJar'.
> Cannot expand ZIP '/Users/nick/Documents/Personal/projects/tsl8r/bar/build/resources/test' as it does not exist.

* Try:
Run with --info or --debug option to get more log output.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':jmhJar'.
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:69)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:35)
    at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:68)
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
    at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:203)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:185)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:66)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:50)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
    at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
Caused by: org.gradle.api.InvalidUserDataException: Cannot expand ZIP '/Users/nick/Documents/Personal/projects/tsl8r/bar/build/resources/test' as it does not exist.
    at org.gradle.api.internal.file.archive.ZipFileTree.visit(ZipFileTree.java:66)
    at org.gradle.api.internal.file.collections.FileTreeAdapter.visit(FileTreeAdapter.java:109)
    at org.gradle.api.internal.file.AbstractFileTree$FilteredFileTreeImpl.visit(AbstractFileTree.java:141)
    at org.gradle.api.internal.file.CompositeFileTree.visit(CompositeFileTree.java:58)
    at org.gradle.api.internal.file.copy.CopySpecActionImpl.execute(CopySpecActionImpl.java:37)
    at org.gradle.api.internal.file.copy.CopySpecActionImpl.execute(CopySpecActionImpl.java:24)
    at org.gradle.api.internal.file.copy.DefaultCopySpec$DefaultCopySpecResolver.walk(DefaultCopySpec.java:498)
    at org.gradle.api.internal.file.copy.DefaultCopySpec$DefaultCopySpecResolver.walk(DefaultCopySpec.java:500)
    at org.gradle.api.internal.file.copy.DefaultCopySpec$DefaultCopySpecResolver.walk(DefaultCopySpec.java:500)
    at org.gradle.api.internal.file.copy.DefaultCopySpec.walk(DefaultCopySpec.java:322)
    at org.gradle.api.internal.file.copy.CopySpecBackedCopyActionProcessingStream.process(CopySpecBackedCopyActionProcessingStream.java:36)
    at org.gradle.api.internal.file.copy.DuplicateHandlingCopyActionDecorator$1.process(DuplicateHandlingCopyActionDecorator.java:44)
    at org.gradle.api.internal.file.copy.NormalizingCopyActionDecorator$1.process(NormalizingCopyActionDecorator.java:56)
    at org.gradle.api.internal.file.archive.ZipCopyAction$1.execute(ZipCopyAction.java:64)
    at org.gradle.api.internal.file.archive.ZipCopyAction$1.execute(ZipCopyAction.java:62)
    at org.gradle.internal.IoActions.withResource(IoActions.java:74)
    at org.gradle.api.internal.file.archive.ZipCopyAction.execute(ZipCopyAction.java:62)
    at org.gradle.api.internal.file.copy.NormalizingCopyActionDecorator.execute(NormalizingCopyActionDecorator.java:52)
    at org.gradle.api.internal.file.copy.DuplicateHandlingCopyActionDecorator.execute(DuplicateHandlingCopyActionDecorator.java:42)
    at org.gradle.api.internal.file.copy.CopyActionExecuter.execute(CopyActionExecuter.java:38)
    at org.gradle.api.tasks.AbstractCopyTask.copy(AbstractCopyTask.java:83)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:75)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.doExecute(AnnotationProcessingTaskFactory.java:228)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:221)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:210)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:585)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:568)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:80)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)
    ... 14 more

benchmarkMode cannot handle multiple values

JMH has the ability to specify multiple benchmarkModes but the Gradle plugin configuration can only handle a single value. PR #62 fixes this according to below:

Before

jmh{
    benchmarkMode = 'thrpt'
    ....
}

After

jmh{
    benchmarkMode = ['thrpt', 'ss']
    ....
}

Does this plugin support multiple profilers?

(NOTE: I am using 0.3.0)

From the docs it looks like it does. So when I use

profilers = [' org.openjdk.jmh.profile.GCProfiler ', ' org.openjdk.jmh.profile.StackProfiler ']

I get

java.lang.ClassNotFoundException:  org.openjdk.jmh.profile.GCProfiler -prof org.openjdk.jmh.profile.StackProfiler 
Profilers failed to initialize, exiting.

Looking at this code in JMHPluginExtension.java

   private void addOption(List<String> options, List values, String option, String separator) {
        if (values!=null) {
            options.add("-"+option);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < values.size(); i++) {
                final Object value = values.get(i);
                sb.append(value);
                if (i<values.size()-1) {
                    sb.append(separator);
                }
            }
            options.add(sb.toString());
        }
    }

should options.add() be called for each value in the List? In addition, a space is required in the first option, (after every separator as well or should the separator include the space?) and after every value i.e.,

options.add("-" + option + " ");
.....
....
sb.append(value + " ");

Open to contributions ?

Hi,

I am currently setting up a gradle project to use jmh, and before writing mine, I bumped into your plugin.
However out of the box there are a couple of reasons which might prevent me from using it, but I'd be glad to contribute if you are ok with the ideas.

1/ source set) What if I have a perf sourceSet that I'd like to use instead of creating the jmh one (hence giving me the ability to customize the source set layout) ? Wouldn't that be a good candidate for a plugin extension, to specify default source set config when none is given ?

2/ task type) It seems that only a single jmh task to run benchmarks is created with the plugin. Wouldn't it be more flexible to create a task type (JmhRunTask for example) that extends JavaExec and expose it directly so that people can create a task with this type themselves ?

3/ config per task) Wouldn't it be convenient to use a configuration object per task to use the extension to configure each task (thus separating task config from plugin config), for those who don't want to create a task explicitely with JmhRunTask type for example.

4/ shadow jar) Following article http://gvsmirnov.ru/blog/tech/2014/03/10/keeping-your-benchmarks-separate.html, I would also find very convenient to be able to expose a jmh jar with batteries included to be able to deploy that easily on a benchmark server for instance without the need for gradle there.

5/ flexible run config) It is indeed convenient to get the type safety when configuring a jmh run task, but it seems to me like you are reverse engineering the option parser spec configured inside the plugin extension. Not only does it seem like a lot of work, but it might be hard to be in sync with the latest jmh public features. Thus I would define the JmhRunTask with basic config options (string array), and then provide 3 different configuration objects in the config :
a) the beautiful way as you did, with type safety, and nice human readable name, with an extra parameter to pass a list of options to solve the 'not yet implemented in the plugin' problem,
b) the flexible mapping, where a human readable mapping is defined in the plugin config (with default mapping defined inside the plugin), and then each task can use those human readable name, which will be converted to short names when building the arg array. It also supports a raw array for those who already like and know the jmh cmdline syntax and don't want to translate it when running on a remote server with shadow jar.

6/ test classes in jar) Sometimes, performance tests share classes with unit tests, and it might be convenient to be able to create a jar with the test outputs.

7/ jmh task group) It is possible in gradle to assign tasks to a given group. Adding tasks to a 'jmh' group here would make things easier to understand for the users (and provide nice locality when running 'gradle tasks')

8/ task desc) Add a description to each task that wich mentions the jmh command line associated with it (to enable copy paste when using shadow jar)

9/ defaults) Define default values at the plugin level which would be applied while building the arg list, should no other value exist

Since an example is worth a 1000 words, here is an example of what I would expect from a jmh plugin configuration after implementing those ideas:

jmh {
    sourceSet = sourceSets.perf
    tasks {
        run3 {
            include = '.*HelloWorld.*'
            threads = 1
            iterations = 1
            warmupIterations = 1
            rawArgs = ['-f 1']
        }
    }
    argNameMappings = [       
        bs : 'batchSize',
        wbs: 'warmupBatchSize',
        i  : ['measurementIterationsNumber','iterations'],  // multiple alias support
        t  : 'threads',
        wi : 'warmupIterations' 
    ]
    argDefaults = [
        i  : 1,
        bm : 'thrpt'
    ]
    mtasks {
        run4 {
            include = ".*HelloWorld.*"
            kvArgs = [
                threads:1,
                wi:1 // support values no defined in mapping (for forward compat among other things)
            ]
            rawArgs = "-f 1" 
        }
        run5 {
            rawArgs = [".*HelloWorld.*", "-t","1","-i", "1", "-wi", "1", "-f", "1"]
        }
        run6 {
            description = "some other desc"
            rawArgs = ".*HelloWorld.* -t 1 -i 1 -wi 1 -f 1"
        }
    }
}

task jmhRun1(type: JmhRunTask, dependsOn: jmhClasses) {
    args = [".*HelloWorld.*", "-t","1","-i", "1", "-wi", "1", "-f", "1"]
}

task jmhRun2(type: JmhRunTask, dependsOn: jmhClasses) {
    args = ".*HelloWorld.* -t 1 -i 1 -wi 1 -f 1"
}

Example of what if would look like in a terminal:

> gradle tasks
...

Jmh tasks
---------
jmhJar - create the jar containing micro benchmark classes, test classes, production classes
jmhJarAll - create a shadow jar containing micro benchmark the output of jmhJar and all its dependencies. Main class is Jmh's Main
jmhRun1 - run jmh. args='.*HelloWorld.* -t 1 -i 1 -wi 1 -f 1'
jmhRun2 - run jmh. args='.*HelloWorld.* -t 1 -i 1 -wi 1 -f 1'
jmhRun3 - run jmh. args='.*HelloWorld.* -t 1 -i 1 -wi 1 -f 1'
jmhRun4 - run jmh. args='.*HelloWorld.* -t 1 -i 1 -wi 1 -f 1 -mb thrpt'
jmhRun5 - run jmh. args='.*HelloWorld.* -t 1 -i 1 -wi 1 -f 1'
jmhRun6 - some other desc. args='.*HelloWorld.* -t 1 -i 1 -wi 1 -f 1'
...

Would you be open to contributions ? If so which of the previous elements would you consider as potential changes to the current plugin ?

Cheers

Aurélien

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.