Skip to content

Commit 700448d

Browse files
committed
Compile benchmark with Dotty, fixes scala#29.
Note, the benchmarks currently don't actually run with Dotty because of scala/scala3#2704. - When `scalaVersion := "0.x"`, then "src/main/dotc" is added to unmanagedSourceDirectories, otherwise "src/main/scalac" is added. - compilation/test checks the setup is OK without the need to remember cli args. - `fork in run` is necessary for -usejavacp to work with compilation/run - `FileVisitOption.FOLLOW_LINKS` produces duplicate filenames, which causes "X is already compiled in this run" errors in Dotty.
1 parent 84a3854 commit 700448d

File tree

6 files changed

+111
-18
lines changed

6 files changed

+111
-18
lines changed

build.sbt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,17 @@ lazy val compilation = addJmh(project).settings(
3636
// We should be able to switch this project to a broad range of Scala versions for comparative
3737
// benchmarking. As such, this project should only depend on the high level `MainClass` compiler API.
3838
description := "Black box benchmark of the compiler",
39-
libraryDependencies += scalaOrganization.value % "scala-compiler" % scalaVersion.value,
40-
mainClass in (Jmh, run) := Some("scala.bench.ScalacBenchmarkRunner")
39+
libraryDependencies += {
40+
if (isDotty.value) "ch.epfl.lamp" %% "dotty-compiler" % scalaVersion.value
41+
else scalaOrganization.value % "scala-compiler" % scalaVersion.value
42+
},
43+
unmanagedSourceDirectories.in(Compile) +=
44+
sourceDirectory.in(Compile).value / (if (isDotty.value) "dotc" else "scalac"),
45+
mainClass in (Jmh, run) := Some("scala.bench.ScalacBenchmarkRunner"),
46+
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % Test,
47+
testOptions in Test += Tests.Argument(TestFrameworks.JUnit),
48+
fork in (Test, test) := true,
49+
fork in run := true
4150
).settings(addJavaOptions).dependsOn(infrastructure)
4251

4352
lazy val micro = addJmh(project).settings(
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package scala.tools.benchmark
2+
3+
import java.io.File
4+
import scala.tools.nsc.BaseBenchmarkDriver
5+
import dotty.tools.dotc.core.Contexts.ContextBase
6+
7+
trait BenchmarkDriver extends BaseBenchmarkDriver {
8+
def compileImpl(): Unit = {
9+
implicit val ctx = (new ContextBase).initialCtx.fresh
10+
ctx.setSetting(ctx.settings.usejavacp, true)
11+
if (depsClasspath != null) {
12+
ctx.setSetting(ctx.settings.classpath,
13+
depsClasspath.mkString(File.pathSeparator))
14+
}
15+
ctx.setSetting(ctx.settings.d, tempDir.getAbsolutePath)
16+
ctx.setSetting(ctx.settings.language, List("Scala2"))
17+
val compiler = new dotty.tools.dotc.Compiler
18+
val reporter =
19+
dotty.tools.dotc.Bench.doCompile(compiler, compilerArgs.toList)
20+
assert(!reporter.hasErrors)
21+
}
22+
}

compilation/src/main/scala/scala/tools/nsc/ScalacBenchmark.scala

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,20 @@ import org.openjdk.jmh.annotations.Mode._
1212
import org.openjdk.jmh.annotations._
1313

1414
import scala.collection.JavaConverters._
15+
import scala.tools.benchmark.BenchmarkDriver
16+
17+
trait BaseBenchmarkDriver {
18+
def source: String
19+
def extraArgs: String
20+
def corpusVersion: String
21+
def depsClasspath: String
22+
def tempDir: File
23+
def corpusSourcePath: Path
24+
def compilerArgs: Array[String]
25+
}
1526

1627
@State(Scope.Benchmark)
17-
class ScalacBenchmark {
28+
class ScalacBenchmark extends BenchmarkDriver {
1829
@Param(value = Array())
1930
var source: String = _
2031

@@ -26,22 +37,9 @@ class ScalacBenchmark {
2637
@Param(value = Array("latest"))
2738
var corpusVersion: String = _
2839

29-
var driver: Driver = _
30-
3140
var depsClasspath: String = _
3241

3342
def compileImpl(): Unit = {
34-
val (compilerArgs, sourceFiles) =
35-
if (source.startsWith("@")) (List(source), List[String]())
36-
else {
37-
import scala.collection.JavaConverters._
38-
val allFiles = Files.walk(findSourceDir, FileVisitOption.FOLLOW_LINKS).collect(Collectors.toList[Path]).asScala.toList
39-
val files = allFiles.filter(f => {
40-
val name = f.getFileName.toString
41-
name.endsWith(".scala") || name.endsWith(".java")
42-
}).map(_.toAbsolutePath.normalize.toString).toList
43-
(List[String](), files)
44-
}
4543

4644
// MainClass is copy-pasted from compiler for source compatibility with 2.10.x - 2.13.x
4745
class MainClass extends Driver with EvalLoop {
@@ -73,7 +71,21 @@ class ScalacBenchmark {
7371
assert(!driver.reporter.hasErrors)
7472
}
7573

76-
private var tempDir: File = null
74+
def compilerArgs: List[String] = if (source.startsWith("@")) List(source) else Nil
75+
76+
def sourceFiles: List[String] =
77+
if (source.startsWith("@")) Nil
78+
else {
79+
import scala.collection.JavaConverters._
80+
val allFiles = Files.walk(findSourceDir, FileVisitOption.FOLLOW_LINKS).collect(Collectors.toList[Path]).asScala.toList
81+
val files = allFiles.filter(f => {
82+
val name = f.getFileName.toString
83+
name.endsWith(".scala") || name.endsWith(".java")
84+
}).map(_.toAbsolutePath.normalize.toString).toList
85+
files
86+
}
87+
88+
var tempDir: File = null
7789

7890
// Executed once per fork
7991
@Setup(Level.Trial) def initTemp(): Unit = {
@@ -86,7 +98,7 @@ class ScalacBenchmark {
8698
BenchmarkUtils.deleteRecursive(tempDir.toPath)
8799
}
88100

89-
private def corpusSourcePath = Paths.get(s"../corpus/$source/$corpusVersion")
101+
def corpusSourcePath: Path = Paths.get(s"../corpus/$source/$corpusVersion")
90102

91103
@Setup(Level.Trial) def initDepsClasspath(): Unit = {
92104
val classPath = BenchmarkUtils.initDeps(corpusSourcePath)
@@ -138,6 +150,8 @@ class ColdScalacBenchmark extends ScalacBenchmark {
138150
@OutputTimeUnit(TimeUnit.MILLISECONDS)
139151
@Warmup(iterations = 0)
140152
@Measurement(iterations = 1, time = 30, timeUnit = TimeUnit.SECONDS)
153+
// @Fork triggers match error in dotty, see https://github.com/lampepfl/dotty/issues/2704
154+
// Comment out `@Fork` to run compilation/test with Dotty or wait for that issue to be fixed.
141155
@Fork(value = 3, jvmArgs = Array("-Xms2G", "-Xmx2G"))
142156
class WarmScalacBenchmark extends ScalacBenchmark {
143157
@Benchmark
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package scala.tools.benchmark
2+
3+
import java.nio.file._
4+
import scala.tools.nsc._
5+
6+
trait BenchmarkDriver extends BaseBenchmarkDriver {
7+
def compileImpl(): Unit = {
8+
// MainClass is copy-pasted from compiler for source compatibility with 2.10.x - 2.13.x
9+
class MainClass extends Driver with EvalLoop {
10+
def resident(compiler: Global): Unit = loop { line =>
11+
val command = new CompilerCommand(line split "\\s+" toList, new Settings(scalacError))
12+
compiler.reporter.reset()
13+
new compiler.Run() compile command.files
14+
}
15+
16+
override def newCompiler(): Global = Global(settings, reporter)
17+
18+
override protected def processSettingsHook(): Boolean = {
19+
if (source == "scala")
20+
settings.sourcepath.value = Paths.get(s"../corpus/$source/$corpusVersion/library").toAbsolutePath.normalize.toString
21+
else
22+
settings.usejavacp.value = true
23+
settings.outdir.value = tempDir.getAbsolutePath
24+
settings.nowarn.value = true
25+
if (depsClasspath != null)
26+
settings.processArgumentString(s"-cp $depsClasspath")
27+
if (extraArgs != null && extraArgs != "")
28+
settings.processArgumentString(extraArgs)
29+
true
30+
}
31+
}
32+
val driver = new MainClass
33+
driver.process(compilerArgs)
34+
assert(!driver.reporter.hasErrors)
35+
}
36+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package scala.tools.benchmark
2+
3+
import scala.tools.nsc.ScalacBenchmarkStandalone
4+
import org.junit.Test
5+
6+
class BenchmarkTest {
7+
@Test def compilesOK =
8+
ScalacBenchmarkStandalone.main(Array("../corpus/vector", "1"))
9+
}

project/plugins.sbt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ logLevel := Level.Warn
33

44
// sbt-jmh plugin - pulls in JMH dependencies too
55
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.25")
6+
7+
// sbt-dotty plugin - to support `scalaVersion := "0.x"`
8+
addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % "0.1.2")

0 commit comments

Comments
 (0)