如何在JUnit测试中使用Scala Maven项目中的JMH?

时间:2018-03-31 02:44:59

标签: scala maven junit jmh

我创建了一个完整的测试项目(version in question linked)。我将这个项目基于以下几个方面:

  1. 同一演示的原始java repository
  2. 生成archetype,例如:

    mvn archetype:generate \
          -DinteractiveMode=false \
          -DarchetypeGroupId=org.openjdk.jmh \
          -DarchetypeArtifactId=jmh-scala-benchmark-archetype \
          -DgroupId=org.sample \
          -DartifactId=test \
          -Dversion=1.0
    
  3. 让测试运行员使用JMH的一种方法(否则甚至不尝试运行):JMH Unable to find the resource: /META-INF/BenchmarkList

  4. 方法的进一步确认:How to run JMH from inside JUnit tests?
  5. Gradle / idea有similar issues,也许。
  6. 也许其他人......我一直盯着这看了好几个小时,但我认为就是这样。
  7. 以下是测试:

    package com.szatmary.peter
    
    import org.junit.Assert
    import org.junit.Test
    import org.openjdk.jmh.annotations.Benchmark
    import org.openjdk.jmh.annotations.Mode
    import org.openjdk.jmh.annotations.Scope
    import org.openjdk.jmh.annotations.State
    import org.openjdk.jmh.results.BenchmarkResult
    import org.openjdk.jmh.results.RunResult
    import org.openjdk.jmh.runner.Runner
    import org.openjdk.jmh.runner.RunnerException
    import org.openjdk.jmh.runner.options.Options
    import org.openjdk.jmh.runner.options.OptionsBuilder
    import org.openjdk.jmh.runner.options.TimeValue
    import org.openjdk.jmh.runner.options.VerboseMode
    import java.util
    import java.util.concurrent.TimeUnit
    
    import com.szatmary.peter.SampleBenchmarkTest.St
    import com.szatmary.peter.SampleBenchmarkTest.St.AVERAGE_EXPECTED_TIME
    
    /**
      * It is recommended to run JMH not from tests but directly from different main method code.
      * Unit-tests and other IDE interferes with the measurements.
      *
      * If your measurements will be in second / minutes or longer than it running nechmarks from tests
      * will not affect your benchmark results.
      *
      * If your measurements will be in  miliseconds, microseconds , nanoseconds ... run your
      * benchmarks rather not from tests bud from main code to have better measurements.
      */
    object SampleBenchmarkTest {
    
      @State(Scope.Benchmark) object St {
        private[peter] val AVERAGE_EXPECTED_TIME = 100 // expected max 100 milliseconds
        val app = new App
    
      }
    
    }
    
    class SampleBenchmarkTest {
      /**
        * Benchmark run with Junit
        *
        * @throws Exception
        */
        @Test
        @throws[Exception]
        def runTest(): Unit = {
          val opt = initBench
          val results = runBench(opt)
          assertOutputs(results)
        }
    
      /**
        * JMH benchmark
        *
        */
      @Benchmark
      def oldWay(st: St.type): Unit = st.app.oldWay
    
      @Benchmark
      def newWay(st: St.type): Unit = st.app.newWay
    
      /**
        * Runner options that runs all benchmarks in this test class
        * namely benchmark oldWay and newWay.
        *
        * @return
        */
      private def initBench: Options = {
        System.out.println("running" + classOf[SampleBenchmarkTest].getSimpleName + ".*")
        return new OptionsBuilder()
          .include(classOf[SampleBenchmarkTest].getSimpleName + ".*")
          .mode(Mode.AverageTime)
          .verbosity(VerboseMode.EXTRA)
          .timeUnit(TimeUnit.MILLISECONDS)
          .warmupTime(TimeValue.seconds(1))
          .measurementTime(TimeValue.milliseconds(1))
          .measurementIterations(2).
          threads(4)
          .warmupIterations(2)
          .shouldFailOnError(true)
          .shouldDoGC(true)
          .forks(1)
          .build
      }
    
      /**
        *
        * @param opt
        * @return
        * @throws RunnerException
        */
      @throws[RunnerException]
      private def runBench(opt: Options) = new Runner(opt).run
    
      /**
        * Assert benchmark results that are interesting for us
        * Asserting test mode and average test time
        *
        * @param results
        */
      private def assertOutputs(results: util.Collection[RunResult]) = {
        import scala.collection.JavaConversions._
        for (r <- results) {
          import scala.collection.JavaConversions._
          for (rr <- r.getBenchmarkResults) {
            val mode = rr.getParams.getMode
            val score = rr.getPrimaryResult.getScore
            val methodName = rr.getPrimaryResult.getLabel
            Assert.assertEquals("Test mode is not average mode. Method = " + methodName, Mode.AverageTime, mode)
            Assert.assertTrue("Benchmark score = " + score + " is higher than " + AVERAGE_EXPECTED_TIME + " " + rr.getScoreUnit + ". Too slow performance !", score < AVERAGE_EXPECTED_TIME)
          }
        }
      }
    }
    

    运行mvn clean install时出错:

    [INFO] --- exec-maven-plugin:1.6.0:exec (run-benchmarks) @ jmh-benchmark-demo ---
    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:98)
            at org.openjdk.jmh.runner.BenchmarkList.find(BenchmarkList.java:122)
            at org.openjdk.jmh.runner.Runner.internalRun(Runner.java:263)
            at org.openjdk.jmh.runner.Runner.run(Runner.java:209)
            at org.openjdk.jmh.Main.main(Main.java:71)
    [ERROR] Command execution failed.
    org.apache.commons.exec.ExecuteException: Process exited with an error: 1 (Exit value: 1)
    

    除非有要求,否则我不打算在这篇文章中包含其他源文件(App.scala - 它只有两种方法对数组进行求和),它可以在github repo中找到。

    为了完整性,这是我目前的pom文件:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.szatmary.peter</groupId>
        <artifactId>jmh-benchmark-demo</artifactId>
        <version>1.0</version>
        <name>JMH benchmark demo</name>
    
        <prerequisites>
            <maven>3.0</maven>
        </prerequisites>
    
        <properties>
            <javac.target>1.8</javac.target>
            <scala.version.major>2.11</scala.version.major>
            <scala.version.minor>11</scala.version.minor>
            <scala.version>
                ${scala.version.major}.${scala.version.minor}
            </scala.version>
    
    
            <!--
        Select a JMH benchmark generator to use. Available options:
           default:    whatever JMH chooses by default;
           asm:        parse bytecode with ASM;
           reflection: load classes and use Reflection over them;
      -->
            <jmh.generator>default</jmh.generator>
    
            <!--
                Name of the benchmark Uber-JAR to generate.
              -->
            <uberjar.name>benchmarks</uberjar.name>
    
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <jmh.version>1.20</jmh.version>
            <java.version>1.8</java.version>
            <junit.version>4.12</junit.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.scala-lang</groupId>
                <artifactId>scala-library</artifactId>
                <version>${scala.version}</version>
            </dependency>
            <dependency>
                <groupId>org.openjdk.jmh</groupId>
                <artifactId>jmh-core</artifactId>
                <version>${jmh.version}</version>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.openjdk.jmh</groupId>
                <artifactId>jmh-generator-annprocess</artifactId>
                <version>${jmh.version}</version>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
    
                <!--
                    1. Add source directories for both scalac and javac.
                -->
    
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>build-helper-maven-plugin</artifactId>
                    <version>1.8</version>
                    <executions>
                        <execution>
                            <id>add-source</id>
                            <phase>generate-sources</phase>
                            <goals>
                                <goal>add-source</goal>
                            </goals>
                            <configuration>
                                <sources>
                                    <source>${project.basedir}/src/main/scala</source>
                                    <source>${project.basedir}/target/generated-sources/jmh</source>
                                </sources>
                            </configuration>
                        </execution>
                        <execution>
                            <id>add-test-source</id>
                            <phase>generate-test-sources</phase>
                            <goals>
                                <goal>add-test-source</goal>
                            </goals>
                            <configuration>
                                <sources>
                                    <source>${project.basedir}/src/test/scala</source>
                                    <source>${project.basedir}/target/generated-test-sources/jmh</source>
                                </sources>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
    
                <!--
                    2. Compile Scala sources
                  -->
    
                <plugin>
                    <groupId>net.alchim31.maven</groupId>
                    <artifactId>scala-maven-plugin</artifactId>
                    <version>3.3.1</version>
                    <configuration>
                        <recompileMode>incremental</recompileMode>
                    </configuration>
                    <executions>
                        <execution>
                            <phase>process-sources</phase>
                            <goals>
                                <goal>compile</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
    
    
                <!--
                    4. Compile JMH generated code.
                 -->
    
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <compilerVersion>${javac.target}</compilerVersion>
                        <source>${javac.target}</source>
                        <target>${javac.target}</target>
                        <compilerArgument>-proc:none</compilerArgument>
                    </configuration>
                </plugin>
    
    
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>exec-maven-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>run-benchmarks</id>
                            <phase>integration-test</phase>
                            <goals>
                                <goal>exec</goal>
                            </goals>
                            <configuration>
                                <classpathScope>test</classpathScope>
                                <executable>java</executable>
                                <arguments>
                                    <argument>-classpath</argument>
                                    <classpath />
                                    <argument>org.openjdk.jmh.Main</argument>
                                    <argument>.*</argument>
                                </arguments>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
    
    
    
            </plugins>
        </build>
    </project>
    

0 个答案:

没有答案