diff --git a/bench/src/main/scala/Benchmarks.scala b/bench/src/main/scala/Benchmarks.scala new file mode 100644 index 000000000000..a29739a93e6e --- /dev/null +++ b/bench/src/main/scala/Benchmarks.scala @@ -0,0 +1,73 @@ +package dotty.tools.benchmarks + +import dotty.tools.dotc._ +import core.Contexts.Context + +import org.openjdk.jmh.results.RunResult +import org.openjdk.jmh.runner.Runner +import org.openjdk.jmh.runner.options.OptionsBuilder +import org.openjdk.jmh.annotations._ +import org.openjdk.jmh.results.format._ +import java.util.concurrent.TimeUnit + +import java.io.{File, FileOutputStream, BufferedWriter, FileWriter} +import scala.collection.JavaConversions._ +import scala.io.Source + +object Bench { + val COMPILE_OPTS_FILE = "compile.txt" + + def main(args: Array[String]): Unit = { + storeCompileOptions(args) + + val libs = System.getenv("BOOTSTRAP_APPEND") + + val opts = new OptionsBuilder() + .jvmArgsPrepend(s"-Xbootclasspath/a:$libs") + .mode(Mode.AverageTime) + .timeUnit(TimeUnit.MICROSECONDS) + .forks(5) + .warmupIterations(5) + .measurementIterations(10) + .resultFormat(ResultFormatType.CSV) + .result("result.csv") + .build + + val runner = new Runner(opts) // full access to all JMH features, you can also provide a custom output Format here + runner.run() // actually run the benchmarks + + removeCompileOptions + } + + def removeCompileOptions: Unit = new File(COMPILE_OPTS_FILE).delete() + + def storeCompileOptions(args: Array[String]): Unit = { + val file = new File(COMPILE_OPTS_FILE) + val bw = new BufferedWriter(new FileWriter(file)) + bw.write(args.mkString("\n")) + bw.close() + } + + def readCompileOptions: Seq[String] = + Source.fromFile(COMPILE_OPTS_FILE).getLines.toSeq +} + +@State(Scope.Benchmark) +class CompilerOptions { + var opts: Array[String] = null + + @Setup + def prepare: Unit = { + opts = Bench.readCompileOptions.to[Array] + } +} + +class Worker extends Driver { + override def newCompiler(implicit ctx: Context): Compiler = new Compiler + + @Benchmark + def compile(state: CompilerOptions): Unit = { + val res = process(state.opts) + if (res.hasErrors) throw new Exception("compilation failed") + } +} diff --git a/bench/templates/launch.mustache b/bench/templates/launch.mustache new file mode 100644 index 000000000000..ceae4fb01488 --- /dev/null +++ b/bench/templates/launch.mustache @@ -0,0 +1,147 @@ +#!/bin/sh +#/*-------------------------------------------------------------------------- +# * Copyright 2012 Taro L. Saito +# * +# * Licensed under the Apache License, Version 2.0 (the "License"); +# * you may not use this file except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.apache.org/licenses/LICENSE-2.0 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# *--------------------------------------------------------------------------*/ + +if [ -z "$PROG_HOME" ] ; then + ## resolve links - $0 may be a link to PROG_HOME + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + PROG_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + PROG_HOME=`cd "$PROG_HOME" && pwd` + + cd "$saveddir" +fi + + +cygwin=false +mingw=false +darwin=false +case "`uname`" in + CYGWIN*) cygwin=true + ;; + MINGW*) mingw=true + ;; + Darwin*) darwin=true + if [ -z "$JAVA_VERSION" ] ; then + JAVA_VERSION="CurrentJDK" + else + echo "Using Java version: $JAVA_VERSION" 1>&2 + fi + if [ -z "$JAVA_HOME" ] ; then + JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/${JAVA_VERSION}/Home + fi + JAVACMD="`which java`" + ;; +esac + +# Resolve JAVA_HOME from javac command path +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" -a -f "$javaExecutable" -a ! "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + javaExecutable="`readlink -f \"$javaExecutable\"`" + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." + echo " We cannot execute $JAVACMD" + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSPATH_SUFFIX="" +# Path separator used in EXTRA_CLASSPATH +PSEP=":" + +# For Cygwin, switch paths to Windows-mixed format before running java +if $cygwin; then + [ -n "$PROG_HOME" ] && + PROG_HOME=`cygpath -am "$PROG_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath -am "$JAVA_HOME"` + CLASSPATH_SUFFIX=";" + PSEP=";" +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$PROG_HOME" ] && + PROG_HOME="`(cd "$PROG_HOME"; pwd -W | sed 's|/|\\\\|g')`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd -W | sed 's|/|\\\\|g')`" + CLASSPATH_SUFFIX=";" + PSEP=";" +fi + + +PROG_NAME={{{PROG_NAME}}} +PROG_VERSION={{{PROG_VERSION}}} +PROG_REVISION={{{PROG_REVISION}}} + +# add default libraries for compilation +export BOOTSTRAP_APPEND="{{{EXPANDED_CLASSPATH}}}${CLASSPATH_SUFFIX}" + +eval exec "\"$JAVACMD\"" \ + {{{JVM_OPTS}}} \ + ${JAVA_OPTS} \ + {{^EXPANDED_CLASSPATH}} + -cp "'{{{EXTRA_CLASSPATH}}}${PROG_HOME}/lib/*${CLASSPATH_SUFFIX}'" \ + {{/EXPANDED_CLASSPATH}} + {{#EXPANDED_CLASSPATH}} + -cp "'{{{EXTRA_CLASSPATH}}}{{{EXPANDED_CLASSPATH}}}${CLASSPATH_SUFFIX}'" \ + {{/EXPANDED_CLASSPATH}} + {{{MAIN_CLASS}}} \"\$@\" +exit $? diff --git a/bench/test/dotty/tools/benchmarks/Benchmarks.scala b/bench/test/dotty/tools/benchmarks/Benchmarks.scala deleted file mode 100644 index c3cf1547127c..000000000000 --- a/bench/test/dotty/tools/benchmarks/Benchmarks.scala +++ /dev/null @@ -1,108 +0,0 @@ -package dotty.tools.benchmarks - -import dotty.tools.StdLibSources -import org.scalameter.Key.reports._ -import org.scalameter.PerformanceTest.OnlineRegressionReport -import org.scalameter.api._ -import org.scalameter.{Context, History, currentContext, persistence} -import org.scalameter.reporting.RegressionReporter.Tester -import dotty.tools.dotc.CompilerTest - -import scala.io.Source -import scala.reflect.io.Directory - -// decorator of persitor to expose info for debugging -class DecoratorPersistor(p: Persistor) extends SerializationPersistor { - - override def load(context: Context): History = { - val resultdir = currentContext(resultDir) - val scope = context.scope - val curve = context.curve - val fileName = s"$resultdir$sep$scope.$curve.dat" - - println(s"load file $fileName") - - p.load(context) - } - - override def save(context: Context, h: History) = { - val resultdir = currentContext(resultDir) - val scope = context.scope - val curve = context.curve - val fileName = s"$resultdir$sep$scope.$curve.dat" - - println(s"save file $fileName") - - p.save(context, h) - } -} - -object BenchTests extends OnlineRegressionReport { - val outputDir = "../out/" - - val compiler = new CompilerTest { - override val defaultOutputDir: String = outputDir - } - - implicit val defaultOptions = List("-d", outputDir) - val scala2mode = List("-language:Scala2") - - val dottyDir = "../compiler/src/dotty/" - val testDir = "../bench/tests/" - - val stdlibFiles = StdLibSources.whitelisted - - def stdLib = compiler.compileList("compileStdLib", stdlibFiles, "-migration" :: "-Yno-inline" :: scala2mode) - - def dotty = compiler.compileDir(dottyDir, ".", List("-deep", "-strict")) - - // NOTE: use `val persistor = ...` would cause persistor ignore command line options for `resultDir` - override def persistor = new DecoratorPersistor(super.persistor) - - // accept all the results, do not fail - override def tester: Tester = new Tester.Accepter - - // store all results - override def historian: RegressionReporter.Historian = RegressionReporter.Historian.Complete() - - override def executor: Executor = LocalExecutor(warmer, aggregator, measurer) - - - def setup = - performance of "dotty" in { - measure.method("stdlib") in { - using(Gen.unit("test")) curve "stdlib" in { r => stdLib } - } - - measure.method("dotty-src") in { - using(Gen.unit("test")) curve "dotty-src" in { r => dotty } - } - - val dir = Directory(testDir) - val fileNames = dir.files.toArray.map(_.jfile.getName).filter(name => (name endsWith ".scala")) - - for (name <- fileNames) { - measure.method(name) in { - using(Gen.unit("test")) curve "dotty" in { r => - compiler.compileFile(testDir, name, extension = "") - } - } - } - } - - /** workaround to fix problem in ScalaMeter - * - * NOTE: Otherwise, command line options would be ignored by HTMLReporter, as - * the HTMLReporter uses the context of tree node, which is created via - * ScalaMeter DSL before command line option `-CresultDir` takes effect - * in `PerformanceTest.main`. - * - * Following code ensures that the test tree is set up after the `-CresultDir` - * option takes effect. - **/ - override def executeTests(): Boolean = { - setup - super.executeTests() - } - -} diff --git a/project/Build.scala b/project/Build.scala index 507b30d8abf1..b0f15229c870 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -20,6 +20,9 @@ import dotty.tools.sbtplugin.DottyIDEPlugin.autoImport._ import org.scalajs.sbtplugin.ScalaJSPlugin import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._ +import pl.project13.scala.sbt.JmhPlugin +import JmhPlugin.JmhKeys.Jmh + /* In sbt 0.13 the Build trait would expose all vals to the shell, where you * can use them in "set a := b" like expressions. This re-exposes them. */ @@ -822,42 +825,19 @@ object Build { ))) lazy val `dotty-bench` = project.in(file("bench")). - dependsOn(`dotty-compiler` % "compile->test"). + dependsOn(`dotty-compiler`). settings(commonNonBootstrappedSettings). settings( - baseDirectory in (Test,run) := (baseDirectory in `dotty-compiler`).value, - - libraryDependencies += "com.storm-enroute" %% "scalameter" % "0.6" % Test, - - fork in Test := true, - parallelExecution in Test := false, - - // http://grokbase.com/t/gg/simple-build-tool/135ke5y90p/sbt-setting-jvm-boot-paramaters-for-scala - javaOptions ++= { - val attList = (dependencyClasspath in Runtime).value - val bin = (packageBin in Compile).value - - // put the Scala {library, reflect, compiler} in the classpath - val path = for { - file <- attList.map(_.data) - path = file.getAbsolutePath - prefix = if (path.endsWith(".jar")) "p" else "a" - } yield "-Xbootclasspath/" + prefix + ":" + path - // dotty itself needs to be in the bootclasspath - val fullpath = ("-Xbootclasspath/a:" + bin) :: path.toList - // System.err.println("BOOTPATH: " + fullpath) - - val ci_build = // propagate if this is a ci build - if (sys.props.isDefinedAt(JENKINS_BUILD)) - List(s"-D$JENKINS_BUILD=${sys.props(JENKINS_BUILD)}") - else if (sys.props.isDefinedAt(DRONE_MEM)) - List("-Xmx" + sys.props(DRONE_MEM)) - else - List() - val res = agentOptions ::: ci_build ::: fullpath - println("Running with javaOptions: " + res) - res - } + mainClass in (Jmh, run) := Some("dotty.tools.benchmarks.Bench") // custom main for jmh:run + ). + enablePlugins(JmhPlugin). + settings(packSettings). + settings( + publishArtifact := false, + packMain := Map("bench" -> "dotty.tools.benchmarks.Bench"), + packGenerateWindowsBatFile := false, + packExpandedClasspath := true, + packBashTemplate := baseDirectory.value + "/templates/launch.mustache" ) // Depend on dotty-library so that sbt projects using dotty automatically diff --git a/project/plugins.sbt b/project/plugins.sbt index 12881ebf2343..6b24922932e3 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -14,3 +14,6 @@ addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "1.1") addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.8.2") + +addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.24") +