Fix incremental compilation of runtime/test (#8975)

This commit is contained in:
Pavel Marek 2024-02-13 10:05:31 +01:00 committed by GitHub
parent cf19115432
commit 5919eda753
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
354 changed files with 1126 additions and 1175 deletions

538
build.sbt
View File

@ -290,6 +290,8 @@ lazy val enso = (project in file("."))
searcher,
launcher,
downloader,
`runtime-integration-tests`,
`runtime-benchmarks`,
`runtime-parser`,
`runtime-compiler`,
`runtime-language-epb`,
@ -320,6 +322,7 @@ lazy val enso = (project in file("."))
`enso-test-java-helpers`,
`exploratory-benchmark-java-helpers`,
`benchmark-java-helpers`,
`benchmarks-common`,
`bench-processor`
)
.settings(Global / concurrentRestrictions += Tags.exclusive(Exclusive))
@ -1571,7 +1574,6 @@ lazy val `runtime-test-instruments` =
)
lazy val runtime = (project in file("engine/runtime"))
.configs(Benchmark)
.enablePlugins(JPMSPlugin)
.settings(
frgaalJavaCompilerSetting,
@ -1584,39 +1586,9 @@ lazy val runtime = (project in file("engine/runtime"))
version := ensoVersion,
commands += WithDebugCommand.withDebug,
inConfig(Compile)(truffleRunOptionsSettings),
// Explicitly provide javafmt task for the custom Benchmark configuration.
// Note that because of the custom Benchmark configuration, the `JavaFormatterPlugin`
// is not able to register this task on its own.
Benchmark / javafmt := {
val streamz = streams.value
val sD = (Benchmark / javafmt / sourceDirectories).value.toList
val iF = (Benchmark / javafmt / includeFilter).value
val eF = (Benchmark / javafmt / excludeFilter).value
val cache = streamz.cacheStoreFactory
val options = (Compile / javafmtOptions).value
JavaFormatter(sD, iF, eF, streamz, cache, options)
},
Benchmark / javafmtCheck := {
val streamz = streams.value
val baseDir = (ThisBuild / baseDirectory).value
val sD = (Benchmark / javafmt / sourceDirectories).value.toList
val iF = (Benchmark / javafmt / includeFilter).value
val eF = (Benchmark / javafmt / excludeFilter).value
val cache = (javafmt / streams).value.cacheStoreFactory
val options = (Compile / javafmtOptions).value
JavaFormatter.check(baseDir, sD, iF, eF, streamz, cache, options)
},
Test / parallelExecution := false,
Test / logBuffered := false,
Test / javaOptions ++= Seq(
"-Dtck.values=java-host,enso",
"-Dtck.language=enso",
"-Dtck.inlineVerifierInstrument=false",
"-Dpolyglot.engine.AllowExperimentalOptions=true"
),
scalacOptions += "-Ymacro-annotations",
scalacOptions ++= Seq("-Ypatmat-exhaust-depth", "off"),
libraryDependencies ++= jmh ++ jaxb ++ GraalVM.langsPkgs ++ Seq(
libraryDependencies ++= GraalVM.langsPkgs ++ Seq(
"org.apache.commons" % "commons-lang3" % commonsLangVersion,
"org.apache.tika" % "tika-core" % tikaVersion,
"com.lihaoyi" %% "fansi" % fansiVersion,
@ -1624,18 +1596,13 @@ lazy val runtime = (project in file("engine/runtime"))
"org.graalvm.sdk" % "polyglot-tck" % graalMavenPackagesVersion % "provided",
"org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion % "provided",
"org.graalvm.truffle" % "truffle-dsl-processor" % graalMavenPackagesVersion % "provided",
"org.graalvm.truffle" % "truffle-tck" % graalMavenPackagesVersion % Test,
"org.graalvm.truffle" % "truffle-tck-common" % graalMavenPackagesVersion % Test,
"org.graalvm.truffle" % "truffle-tck-tests" % graalMavenPackagesVersion % Test,
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion % "provided",
"org.scalacheck" %% "scalacheck" % scalacheckVersion % Test,
"org.scalactic" %% "scalactic" % scalacticVersion % Test,
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
"org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion % Benchmark,
"junit" % "junit" % junitVersion % Test,
"com.github.sbt" % "junit-interface" % junitIfVersion % Test,
"org.hamcrest" % "hamcrest-all" % hamcrestVersion % Test,
"org.slf4j" % "slf4j-nop" % slf4jVersion % Benchmark,
"org.slf4j" % "slf4j-api" % slf4jVersion % Test
),
// Add all GraalVM packages with Runtime scope - we don't need them for compilation,
@ -1650,109 +1617,6 @@ lazy val runtime = (project in file("engine/runtime"))
necessaryModules ++ langs ++ tools
}
)
.settings(
Test / unmanagedClasspath := (LocalProject(
"runtime-fat-jar"
) / Compile / exportedProducts).value,
Test / addModules := Seq(
(`runtime-test-instruments` / javaModuleName).value,
(`runtime-fat-jar` / javaModuleName).value
),
Test / modulePath := {
val updateReport = (Test / update).value
val requiredModIds =
GraalVM.modules ++ GraalVM.langsPkgs ++ GraalVM.insightPkgs ++ logbackPkg ++ Seq(
"org.slf4j" % "slf4j-api" % slf4jVersion,
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion,
"org.graalvm.sdk" % "polyglot-tck" % graalMavenPackagesVersion,
"org.graalvm.truffle" % "truffle-tck" % graalMavenPackagesVersion,
"org.graalvm.truffle" % "truffle-tck-common" % graalMavenPackagesVersion,
"org.graalvm.truffle" % "truffle-tck-tests" % graalMavenPackagesVersion
)
val requiredMods = JPMSUtils.filterModulesFromUpdate(
updateReport,
requiredModIds,
streams.value.log,
shouldContainAll = true
)
val runtimeTestInstrumentsMod =
(`runtime-test-instruments` / Compile / exportedProducts).value.head.data
val runtimeMod =
(`runtime-fat-jar` / Compile / exportedProducts).value.head.data
requiredMods ++
Seq(runtimeTestInstrumentsMod) ++
Seq(runtimeMod)
},
Test / patchModules := {
/** All these modules will be in --patch-module cmdline option to java, which means that
* for the JVM, it will appear that all the classes contained in these sbt projects are contained
* in the `org.enso.runtime` module. In this way, we do not have to assembly the `runtime.jar`
* fat jar.
*/
val modulesToPatchIntoRuntime: Seq[File] =
(LocalProject(
"runtime-instrument-common"
) / Compile / productDirectories).value ++
(LocalProject(
"runtime-instrument-id-execution"
) / Compile / productDirectories).value ++
(LocalProject(
"runtime-instrument-repl-debugger"
) / Compile / productDirectories).value ++
(LocalProject(
"runtime-instrument-runtime-server"
) / Compile / productDirectories).value ++
(LocalProject(
"runtime-language-epb"
) / Compile / productDirectories).value ++
(LocalProject(
"runtime-compiler"
) / Compile / productDirectories).value ++
(LocalProject(
"refactoring-utils"
) / Compile / productDirectories).value ++
(LocalProject(
"runtime-instrument-common"
) / Test / productDirectories).value
// Patch test-classes into the runtime module. This is standard way to deal with the
// split package problem in unit tests. For example, Maven's surefire plugin does this.
val testClassesDir = (Test / productDirectories).value.head
Map(
(`runtime-fat-jar` / javaModuleName).value -> (modulesToPatchIntoRuntime ++ Seq(
testClassesDir
))
)
},
Test / addReads := {
val runtimeModName = (`runtime-fat-jar` / javaModuleName).value
val testInstrumentsModName =
(`runtime-test-instruments` / javaModuleName).value
Map(
// We patched the test-classes into the runtime module. These classes access some stuff from
// unnamed module. Thus, let's add ALL-UNNAMED.
runtimeModName -> Seq(
"ALL-UNNAMED",
testInstrumentsModName,
"truffle.tck.tests",
"org.openide.util.lookup.RELEASE180"
),
testInstrumentsModName -> Seq(runtimeModName)
)
},
Test / javaOptions ++= testLogProviderOptions
)
.settings(
Test / fork := true,
Test / envVars ++= distributionEnvironmentOverrides ++ Map(
"ENSO_TEST_DISABLE_IR_CACHE" -> "false",
"ENSO_EDITION_PATH" -> file("distribution/editions").getCanonicalPath
),
Test / compile := {
(LocalProject("runtime-instrument-common") / Test / compile).value
(Test / compile).value
}
)
.settings(
(Compile / javacOptions) ++= Seq(
"-s",
@ -1778,50 +1642,6 @@ lazy val runtime = (project in file("engine/runtime"))
.dependsOn(`std-aws` / Compile / packageBin)
.value
)
/** Benchmark settings */
.settings(
inConfig(Benchmark)(Defaults.testSettings),
Benchmark / javacOptions --= Seq(
"-source",
frgaalSourceLevel,
"--enable-preview"
),
(Benchmark / javaOptions) :=
(LocalProject("std-benchmarks") / Benchmark / run / javaOptions).value,
(Benchmark / javaOptions) ++= benchOnlyOptions,
Benchmark / fork := true,
Benchmark / parallelExecution := false,
// This ensures that the full class-path of runtime-fat-jar is put on
// class-path of the Java compiler (and thus the benchmark annotation processor).
(Benchmark / compile / unmanagedClasspath) ++=
(LocalProject(
"runtime-fat-jar"
) / Compile / fullClasspath).value,
bench := (Benchmark / test)
.tag(Exclusive)
.dependsOn(
// runtime.jar fat jar needs to be assembled as it is used in the
// benchmarks. This dependency is here so that `runtime/bench` works
// after clean build.
LocalProject("runtime-fat-jar") / assembly
)
.value,
(Benchmark / testOnly) := (Benchmark / testOnly)
.dependsOn(
LocalProject("runtime-fat-jar") / assembly
)
.evaluated,
benchOnly := Def.inputTaskDyn {
import complete.Parsers.spaceDelimited
val name = spaceDelimited("<name>").parsed match {
case List(name) => name
case _ => throw new IllegalArgumentException("Expected one argument.")
}
Def.task {
(Benchmark / testOnly).toTask(" -- -z " + name).value
}
}.evaluated
)
.dependsOn(`common-polyglot-core-utils`)
.dependsOn(`edition-updater`)
.dependsOn(`interpreter-dsl`)
@ -1833,8 +1653,238 @@ lazy val runtime = (project in file("engine/runtime"))
.dependsOn(`runtime-compiler`)
.dependsOn(`connected-lock-manager`)
.dependsOn(testkit % Test)
.dependsOn(`logging-service-logback` % "test->test")
.dependsOn(`runtime-test-instruments` % "test->compile")
/** A project holding all the runtime integration tests. These tests require, among other things,
* the `org.enso.runtime` JPMS module, so it is easier to keep them in a separate project.
* For standard unit tests, use `runtime/Test`.
*/
lazy val `runtime-integration-tests` =
(project in file("engine/runtime-integration-tests"))
.enablePlugins(JPMSPlugin)
.settings(
frgaalJavaCompilerSetting,
Compile / logManager :=
sbt.internal.util.CustomLogManager.excludeMsg(
"Could not determine source for class ",
Level.Warn
),
commands += WithDebugCommand.withDebug,
libraryDependencies ++= GraalVM.modules ++ GraalVM.langsPkgs ++ GraalVM.insightPkgs ++ logbackPkg ++ Seq(
"org.graalvm.polyglot" % "polyglot" % graalMavenPackagesVersion % "provided",
"org.graalvm.sdk" % "polyglot-tck" % graalMavenPackagesVersion % "provided",
"org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion % "provided",
"org.graalvm.truffle" % "truffle-dsl-processor" % graalMavenPackagesVersion % "provided",
"org.graalvm.truffle" % "truffle-tck" % graalMavenPackagesVersion % Test,
"org.graalvm.truffle" % "truffle-tck-common" % graalMavenPackagesVersion % Test,
"org.graalvm.truffle" % "truffle-tck-tests" % graalMavenPackagesVersion % Test,
"org.scalacheck" %% "scalacheck" % scalacheckVersion % Test,
"org.scalactic" %% "scalactic" % scalacticVersion % Test,
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
"junit" % "junit" % junitVersion % Test,
"com.github.sbt" % "junit-interface" % junitIfVersion % Test,
"org.hamcrest" % "hamcrest-all" % hamcrestVersion % Test,
"org.slf4j" % "slf4j-api" % slf4jVersion % Test
),
Test / javacOptions ++= Seq(
"-s",
(Compile / sourceManaged).value.getAbsolutePath,
"-Xlint:unchecked"
),
Test / compile := (Test / compile)
.dependsOn(Def.task { (Compile / sourceManaged).value.mkdirs })
.dependsOn(`runtime-fat-jar` / Compile / compileModuleInfo)
.value,
Test / fork := true,
Test / parallelExecution := false,
Test / logBuffered := false,
Test / envVars ++= distributionEnvironmentOverrides ++ Map(
"ENSO_TEST_DISABLE_IR_CACHE" -> "false",
"ENSO_EDITION_PATH" -> file("distribution/editions").getCanonicalPath
),
inConfig(Test)(truffleRunOptionsSettings),
Test / javaOptions ++= Seq(
"-Dtck.values=java-host,enso",
"-Dtck.language=enso",
"-Dtck.inlineVerifierInstrument=false",
"-Dpolyglot.engine.AllowExperimentalOptions=true"
),
Test / javaOptions ++= testLogProviderOptions,
Test / addModules := Seq(
(`runtime-test-instruments` / javaModuleName).value,
(`runtime-fat-jar` / javaModuleName).value
),
Test / modulePath := {
val updateReport = (Test / update).value
val requiredModIds =
GraalVM.modules ++ GraalVM.langsPkgs ++ GraalVM.insightPkgs ++ logbackPkg ++ Seq(
"org.slf4j" % "slf4j-api" % slf4jVersion,
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion,
"org.graalvm.sdk" % "polyglot-tck" % graalMavenPackagesVersion,
"org.graalvm.truffle" % "truffle-tck" % graalMavenPackagesVersion,
"org.graalvm.truffle" % "truffle-tck-common" % graalMavenPackagesVersion,
"org.graalvm.truffle" % "truffle-tck-tests" % graalMavenPackagesVersion
)
val requiredMods = JPMSUtils.filterModulesFromUpdate(
updateReport,
requiredModIds,
streams.value.log,
shouldContainAll = true
)
val runtimeTestInstrumentsMod =
(`runtime-test-instruments` / Compile / exportedProducts).value.head.data
val runtimeMod =
(`runtime-fat-jar` / Compile / exportedProducts).value.head.data
requiredMods ++
Seq(runtimeTestInstrumentsMod) ++
Seq(runtimeMod)
},
Test / patchModules := {
/** All these modules will be in --patch-module cmdline option to java, which means that
* for the JVM, it will appear that all the classes contained in these sbt projects are contained
* in the `org.enso.runtime` module. In this way, we do not have to assembly the `runtime.jar`
* fat jar.
*/
val modulesToPatchIntoRuntime: Seq[File] =
(LocalProject(
"runtime-instrument-common"
) / Compile / productDirectories).value ++
(LocalProject(
"runtime-instrument-id-execution"
) / Compile / productDirectories).value ++
(LocalProject(
"runtime-instrument-repl-debugger"
) / Compile / productDirectories).value ++
(LocalProject(
"runtime-instrument-runtime-server"
) / Compile / productDirectories).value ++
(LocalProject(
"runtime-language-epb"
) / Compile / productDirectories).value ++
(LocalProject(
"runtime-compiler"
) / Compile / productDirectories).value ++
(LocalProject(
"refactoring-utils"
) / Compile / productDirectories).value ++
(LocalProject(
"runtime-instrument-common"
) / Test / productDirectories).value
// Patch test-classes into the runtime module. This is standard way to deal with the
// split package problem in unit tests. For example, Maven's surefire plugin does this.
val testClassesDir = (Test / productDirectories).value.head
Map(
(`runtime-fat-jar` / javaModuleName).value -> (modulesToPatchIntoRuntime ++ Seq(
testClassesDir
))
)
},
Test / addReads := {
val runtimeModName = (`runtime-fat-jar` / javaModuleName).value
val testInstrumentsModName =
(`runtime-test-instruments` / javaModuleName).value
Map(
// We patched the test-classes into the runtime module. These classes access some stuff from
// unnamed module. Thus, let's add ALL-UNNAMED.
runtimeModName -> Seq(
"ALL-UNNAMED",
testInstrumentsModName,
"truffle.tck.tests",
"org.openide.util.lookup.RELEASE180"
),
testInstrumentsModName -> Seq(runtimeModName)
)
}
)
.dependsOn(`runtime-fat-jar`)
.dependsOn(`runtime-test-instruments`)
.dependsOn(`logging-service-logback` % "test->test")
.dependsOn(testkit % Test)
/** A project that holds only benchmarks for `runtime`. Unlike `runtime-integration-tests`, its execution requires
* the whole `runtime-fat-jar` assembly, as we want to be as close to the enso distribution as possible.
*/
lazy val `runtime-benchmarks` =
(project in file("engine/runtime-benchmarks"))
.enablePlugins(JPMSPlugin)
.settings(
frgaalJavaCompilerSetting,
// Note that withDebug command only makes sense if you use `@Fork(0)` in your benchmarks.
commands += WithDebugCommand.withDebug,
libraryDependencies ++= GraalVM.modules ++ GraalVM.langsPkgs ++ GraalVM.toolsPkgs ++ Seq(
"org.openjdk.jmh" % "jmh-core" % jmhVersion,
"org.openjdk.jmh" % "jmh-generator-annprocess" % jmhVersion,
"jakarta.xml.bind" % "jakarta.xml.bind-api" % jaxbVersion,
"com.sun.xml.bind" % "jaxb-impl" % jaxbVersion,
"org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion,
"org.slf4j" % "slf4j-api" % slf4jVersion,
"org.slf4j" % "slf4j-nop" % slf4jVersion
),
mainClass :=
Some("org.enso.interpreter.bench.benchmarks.RuntimeBenchmarksRunner"),
Compile / logManager :=
sbt.internal.util.CustomLogManager.excludeMsg(
"Could not determine source for class ",
Level.Warn
),
javacOptions --= Seq(
"-source",
frgaalSourceLevel,
"--enable-preview"
),
parallelExecution := false,
modulePath := {
val requiredModIds = GraalVM.modules ++ GraalVM.langsPkgs ++ Seq(
"org.slf4j" % "slf4j-api" % slf4jVersion,
"org.slf4j" % "slf4j-nop" % slf4jVersion
)
val requiredMods = JPMSUtils.filterModulesFromUpdate(
(Compile / update).value,
requiredModIds,
streams.value.log,
shouldContainAll = true
)
val runtimeMod =
(`runtime-fat-jar` / assembly / assemblyOutputPath).value
requiredMods ++ Seq(runtimeMod)
},
addModules := {
val runtimeModuleName = (`runtime-fat-jar` / javaModuleName).value
Seq(runtimeModuleName)
},
addExports := {
Map("org.slf4j.nop/org.slf4j.nop" -> Seq("org.slf4j"))
},
javaOptions ++= {
Seq(
// To enable logging in benchmarks, add ch.qos.logback module on the modulePath
"-Dslf4j.provider=org.slf4j.nop.NOPServiceProvider"
)
},
javaOptions ++= benchOnlyOptions,
run / fork := true,
run / connectInput := true,
bench := Def
.task {
(Compile / run).toTask("").tag(Exclusive).value
}
.dependsOn(
buildEngineDistribution
)
.value,
benchOnly := Def.inputTaskDyn {
import complete.Parsers.spaceDelimited
val name = spaceDelimited("<name>").parsed match {
case List(name) => name
case _ => throw new IllegalArgumentException("Expected one argument.")
}
Def.task {
(Compile / run).toTask(" " + name).value
}
}.evaluated
)
.dependsOn(`runtime-fat-jar`)
.dependsOn(`benchmarks-common`)
lazy val `runtime-parser` =
(project in file("engine/runtime-parser"))
@ -1895,7 +1945,7 @@ lazy val `runtime-instrument-common` =
bench := (Benchmark / test).tag(Exclusive).value,
Benchmark / parallelExecution := false,
(Benchmark / javaOptions) :=
(LocalProject("std-benchmarks") / Benchmark / run / javaOptions).value,
(LocalProject("std-benchmarks") / Compile / javaOptions).value,
Test / fork := true,
Test / envVars ++= distributionEnvironmentOverrides ++ Map(
"ENSO_TEST_DISABLE_IR_CACHE" -> "false"
@ -2258,6 +2308,19 @@ lazy val `distribution-manager` = project
.dependsOn(pkg)
.dependsOn(`logging-utils`)
lazy val `benchmarks-common` =
(project in file("lib/java/benchmarks-common"))
.settings(
frgaalJavaCompilerSetting,
libraryDependencies ++= GraalVM.modules ++ Seq(
"org.openjdk.jmh" % "jmh-core" % jmhVersion,
"org.openjdk.jmh" % "jmh-generator-annprocess" % jmhVersion,
"jakarta.xml.bind" % "jakarta.xml.bind-api" % jaxbVersion,
"com.sun.xml.bind" % "jaxb-impl" % jaxbVersion
)
)
.dependsOn(`polyglot-api`)
lazy val `bench-processor` = (project in file("lib/scala/bench-processor"))
.settings(
frgaalJavaCompilerSetting,
@ -2292,28 +2355,21 @@ lazy val `bench-processor` = (project in file("lib/scala/bench-processor"))
(Test / unmanagedClasspath) :=
(LocalProject("runtime-fat-jar") / Compile / fullClasspath).value
)
.dependsOn(`benchmarks-common`)
.dependsOn(`polyglot-api`)
.dependsOn(runtime)
lazy val `std-benchmarks` = (project in file("std-bits/benchmarks"))
.enablePlugins(JPMSPlugin)
.settings(
frgaalJavaCompilerSetting,
libraryDependencies ++= jmh ++ Seq(
"org.openjdk.jmh" % "jmh-core" % jmhVersion % Benchmark,
"org.openjdk.jmh" % "jmh-generator-annprocess" % jmhVersion % Benchmark,
"org.graalvm.polyglot" % "polyglot" % graalMavenPackagesVersion % Benchmark,
"org.slf4j" % "slf4j-api" % slf4jVersion % Benchmark,
"org.slf4j" % "slf4j-nop" % slf4jVersion % Benchmark
libraryDependencies ++= GraalVM.modules ++ GraalVM.langsPkgs ++ Seq(
"org.openjdk.jmh" % "jmh-core" % jmhVersion,
"org.openjdk.jmh" % "jmh-generator-annprocess" % jmhVersion,
"org.graalvm.polyglot" % "polyglot" % graalMavenPackagesVersion,
"org.slf4j" % "slf4j-api" % slf4jVersion,
"org.slf4j" % "slf4j-nop" % slf4jVersion
),
// Add all GraalVM packages with Benchmark scope - we don't need them for compilation,
// just provide them for benchmarks (in module-path).
libraryDependencies ++= {
val necessaryModules =
GraalVM.modules.map(_.withConfigurations(Some(Benchmark.name)))
val langs =
GraalVM.langsPkgs.map(_.withConfigurations(Some(Benchmark.name)))
necessaryModules ++ langs
},
commands += WithDebugCommand.withDebug,
(Compile / logManager) :=
sbt.internal.util.CustomLogManager.excludeMsg(
@ -2321,67 +2377,56 @@ lazy val `std-benchmarks` = (project in file("std-bits/benchmarks"))
Level.Warn
)
)
.configs(Benchmark)
.settings(
inConfig(Benchmark)(Defaults.testSettings)
)
.settings(
(Benchmark / parallelExecution) := false,
(Benchmark / run / fork) := true,
(Benchmark / run / connectInput) := true,
// This ensures that the full class-path of runtime-fat-jar is put on
// class-path of the Java compiler (and thus the benchmark annotation processor).
(Benchmark / compile / unmanagedClasspath) ++=
(LocalProject(
"runtime-fat-jar"
) / Compile / fullClasspath).value,
(Benchmark / compile / javacOptions) ++= Seq(
parallelExecution := false,
run / fork := true,
run / connectInput := true,
mainClass :=
(LocalProject("bench-processor") / mainClass).value,
Compile / compile := (Compile / compile)
.dependsOn(`runtime-fat-jar` / assembly)
.value,
Compile / javacOptions ++= Seq(
"-s",
(Benchmark / sourceManaged).value.getAbsolutePath,
(Compile / sourceManaged).value.getAbsolutePath,
"-Xlint:unchecked",
"-J-Dpolyglotimpl.DisableClassPathIsolation=true",
"-J-Dpolyglot.engine.WarnInterpreterOnly=false"
),
(Benchmark / run / javaOptions) ++= {
val requiredModules = GraalVM.modules ++ GraalVM.langsPkgs ++ Seq(
modulePath := {
val requiredModIds = GraalVM.modules ++ GraalVM.langsPkgs ++ Seq(
"org.slf4j" % "slf4j-api" % slf4jVersion,
"org.slf4j" % "slf4j-nop" % slf4jVersion
)
val requiredModulesCp = JPMSUtils.filterModulesFromClasspath(
(Benchmark / fullClasspath).value,
requiredModules,
val requiredMods = JPMSUtils.filterModulesFromUpdate(
(Compile / update).value,
requiredModIds,
streams.value.log,
shouldContainAll = true
)
val requiredModulesPaths = requiredModulesCp
.map(_.data.getAbsolutePath)
val runtimeJar =
(LocalProject(
"runtime-fat-jar"
) / assembly / assemblyOutputPath).value.getAbsolutePath
val allModulePaths = requiredModulesPaths ++ Seq(runtimeJar)
val runtimeModuleName =
(LocalProject(
"runtime-fat-jar"
) / javaModuleName).value
val runtimeMod =
(`runtime-fat-jar` / assembly / assemblyOutputPath).value
requiredMods ++ Seq(runtimeMod)
},
addModules := {
val runtimeModuleName = (`runtime-fat-jar` / javaModuleName).value
Seq(runtimeModuleName)
},
addExports := {
Map("org.slf4j.nop/org.slf4j.nop" -> Seq("org.slf4j"))
},
javaOptions ++= {
Seq(
// To enable logging in benchmarks, add ch.qos.logback module on the modulePath
"-Dslf4j.provider=org.slf4j.nop.NOPServiceProvider",
"--module-path",
allModulePaths.mkString(File.pathSeparator),
"--add-modules",
runtimeModuleName,
"--add-exports",
"org.slf4j.nop/org.slf4j.nop=org.slf4j"
"-Dslf4j.provider=org.slf4j.nop.NOPServiceProvider"
)
},
(Benchmark / run / mainClass) :=
(LocalProject("bench-processor") / mainClass).value
javaOptions ++= benchOnlyOptions
)
.settings(
bench := Def
.task {
(Benchmark / run).toTask("").tag(Exclusive).value
(Compile / run).toTask("").tag(Exclusive).value
}
.dependsOn(
buildEngineDistribution
@ -2394,12 +2439,13 @@ lazy val `std-benchmarks` = (project in file("std-bits/benchmarks"))
case _ => throw new IllegalArgumentException("Expected one argument.")
}
Def.task {
(Benchmark / run).toTask(" " + name).value
(Compile / run).toTask(" " + name).value
}
}.evaluated
)
.dependsOn(`bench-processor` % Benchmark)
.dependsOn(runtime % Benchmark)
.dependsOn(`bench-processor`)
.dependsOn(`runtime-fat-jar`)
.dependsOn(`std-table`)
lazy val editions = project
.in(file("lib/scala/editions"))

View File

@ -91,6 +91,7 @@
Factorial.enso:
runtime/:
target/:
runtime-benchmarks/:
bench-report.xml:
lib/:
rust/:

View File

@ -105,7 +105,7 @@ impl Benchmarks {
pub fn sbt_task(self) -> Option<&'static str> {
match self {
Benchmarks::All => Some("bench"),
Benchmarks::Runtime => Some("runtime/bench"),
Benchmarks::Runtime => Some("runtime-benchmarks/bench"),
Benchmarks::Enso => None,
Benchmarks::EnsoJMH => Some("std-benchmarks/bench"),
}

View File

@ -448,7 +448,7 @@ impl RunContext {
} else {
if self.config.build_benchmarks {
// Check Runtime Benchmark Compilation
sbt.call_arg("runtime/Benchmark/compile").await?;
sbt.call_arg("runtime-benchmarks/compile").await?;
// Check Language Server Benchmark Compilation
sbt.call_arg("language-server/Benchmark/compile").await?;
@ -478,8 +478,12 @@ impl RunContext {
for bench in &self.config.execute_benchmarks {
match bench {
Benchmarks::Runtime => {
let runtime_bench_report =
&self.paths.repo_root.engine.runtime.bench_report_xml;
let runtime_bench_report = &self
.paths
.repo_root
.engine
.join("runtime-benchmarks")
.join("bench-report.xml");
if runtime_bench_report.exists() {
ide_ci::actions::artifacts::upload_single_file(
runtime_bench_report,

View File

@ -13,26 +13,38 @@ To track the performance of the engine, we use
benchmarks:
- [micro benchmarks](#engine-jmh-microbenchmarks) located directly in the
`runtime` SBT project. These benchmarks are written in Java, and are used to
measure the performance of specific parts of the engine.
`runtime-benchmarks` SBT project. These benchmarks are written in Java, and
are used to measure the performance of specific parts of the engine.
- [standard library benchmarks](#standard-library-benchmarks) located in the
`test/Benchmarks` Enso project. These benchmarks are entirelly written in
Enso, along with the harness code.
`test/Benchmarks` Enso project. These benchmarks are entirely written in Enso,
along with the harness code.
## Engine JMH microbenchmarks
These benchmarks are written in Java and are used to measure the performance of
specific parts of the engine. The sources are located in the `runtime` SBT
project, under `src/bench` source directory.
specific parts of the engine. The sources are located in the
`runtime-benchmarks` SBT project, under `engine/runtime-benchmarks` directory.
### Running the benchmarks
To run the benchmarks, use `bench` or `benchOnly` command - `bench` runs all the
benchmarks and `benchOnly` runs only one benchmark specified with the fully
qualified name. The parameters for these benchmarks are hard-coded inside the
JMH annotations in the source files. In order to change, e.g., the number of
measurement iterations, you need to modify the parameter to the `@Measurement`
annotation.
To run the benchmarks, use `bench` or `benchOnly` command in the
`runtime-benchmarks` project - `bench` runs all the benchmarks and `benchOnly`
runs only one benchmark specified with the fully qualified name. The
aforementioned commands are mere shortcuts to the
[standard JMH launcher](https://github.com/openjdk/jmh/blob/master/jmh-core/src/main/java/org/openjdk/jmh/Main.java).
To get the full power of the JMH launcher, invoke simply `run` with cmdline
options passed to the launcher. For the full options summary, see the
[JMH source code](https://github.com/openjdk/jmh/blob/master/jmh-core/src/main/java/org/openjdk/jmh/runner/options/CommandLineOptions.java),
or invoke `run -h`.
You can change the parameters to the benchmarks either by modifying the
annotations directly in the source code, or by passing the parameters to the JMH
runner. For example, to run the benchmarks with 3 warmup iterations and 2
measurement iterations, use:
```
sbt:runtime-benchmarks> run -w 3 -i 2 <bench-name>
```
### Debugging the benchmarks
@ -94,12 +106,12 @@ the full options summary, either see the
or run the launcher with `-h` option.
The `std-benchmarks` SBT project supports `bench` and `benchOnly` commands, that
work the same as in the `runtime` project, with the exception that the benchmark
name does not have to be specified as a fully qualified name, but as a regular
expression. To access the full flexibility of the JMH launcher, run it via
`Bench/run` - for example, to see the help message: `Bench/run -h`. For example,
you can run all the benchmarks that have "New_Vector" in their name with just 3
seconds for warmup iterations and 2 measurement iterations with
work the same as in the `runtime-benchmarks` project, with the exception that
the benchmark name does not have to be specified as a fully qualified name, but
as a regular expression. To access the full flexibility of the JMH launcher, run
it via `Bench/run` - for example, to see the help message: `Bench/run -h`. For
example, you can run all the benchmarks that have "New_Vector" in their name
with just 3 seconds for warmup iterations and 2 measurement iterations with
`Bench/run -w 3 -i 2 New_Vector`.
Whenever you add or delete any benchmarks from `test/Benchmarks` project, the

View File

@ -0,0 +1,10 @@
package org.enso.interpreter.bench.benchmarks;
import org.enso.interpreter.bench.BenchmarksRunner;
import org.openjdk.jmh.runner.RunnerException;
public class RuntimeBenchmarksRunner {
public static void main(String[] args) throws RunnerException {
BenchmarksRunner.run(args);
}
}

View File

@ -0,0 +1,285 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.enso.interpreter.bench.Utils;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.Blackhole;
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
@Warmup(iterations = 5)
@Measurement(iterations = 5)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class AtomBenchmarks {
private static final Long MILLION = 1_000_000L;
private static final String MILLION_ELEMENT_LIST =
"""
import Standard.Base.Data.List.List
import Standard.Base.Data.Numbers
main =
generator fn acc i end = if i == end then acc else @Tail_Call generator fn (fn acc i) i+1 end
res = generator (acc -> x -> List.Cons x acc) List.Nil 1 $million
res
"""
.replace("$million", MILLION.toString());
private static final String GENERATE_LIST_CODE =
"""
import Standard.Base.Data.List.List
import Standard.Base.Data.Numbers
main = length ->
generator = acc -> i -> if i == 0 then acc else @Tail_Call generator (List.Cons i acc) (i - 1)
res = generator List.Nil length
res
""";
private static final String GENERATE_LIST_QUALIFIED_CODE =
"""
import Standard.Base.Data.List.List
import Standard.Base.Data.Numbers
main = length ->
generator = acc -> i -> if i == 0 then acc else @Tail_Call generator (List.Cons i acc) (i - 1)
res = generator List.Nil length
res
""";
private static final String REVERSE_LIST_CODE =
"""
import Standard.Base.Data.List.List
import Standard.Base.Data.Numbers
main = list ->
reverser = acc -> list -> case list of
List.Cons h t -> @Tail_Call reverser (List.Cons h acc) t
List.Nil -> acc
res = reverser List.Nil list
res
""";
private static final String REVERSE_LIST_METHODS_CODE =
"""
import Standard.Base.Data.List.List
import Standard.Base.Data.Numbers
List.rev self acc = case self of
List.Cons h t -> @Tail_Call t.rev (List.Cons h acc)
_ -> acc
main = list ->
res = list.rev List.Nil
res
""";
private static final String SUM_LIST_CODE =
"""
import Standard.Base.Data.List.List
import Standard.Base.Data.Numbers
main = list ->
summator = acc -> list -> case list of
List.Cons h t -> @Tail_Call summator acc+h t
List.Nil -> acc
res = summator 0 list
res
""";
private static final String SUM_LIST_LEFT_FOLD_CODE =
"""
import Standard.Base.Data.List.List
import Standard.Base.Data.Numbers
main = list ->
fold = f -> acc -> list -> case list of
List.Cons h t -> @Tail_Call fold f (f acc h) t
_ -> acc
res = fold (x -> y -> x + y) 0 list
res
""";
private static final String SUM_LIST_FALLBACK_CODE =
"""
import Standard.Base.Data.List.List
import Standard.Base.Data.Numbers
main = list ->
summator = acc -> list -> case list of
List.Cons h t -> @Tail_Call summator acc+h t
_ -> acc
res = summator 0 list
res
""";
private static final String SUM_LIST_METHODS_CODE =
"""
import Standard.Base.Data.List.List
import Standard.Base.Data.Numbers
List.sum self acc = case self of
List.Cons h t -> @Tail_Call t.sum h+acc
_ -> acc
main = list ->
res = list.sum 0
res
""";
private static final String MAP_REVERSE_LIST_CODE =
"""
import Standard.Base.Data.List.List
import Standard.Base.Data.Numbers
List.mapReverse self f acc = case self of
List.Cons h t -> @Tail_Call t.mapReverse f (List.Cons (f h) acc)
_ -> acc
main = list ->
res = list.mapReverse (x -> x + 1) List.Nil
res
""";
private static final String MAP_REVERSE_LIST_CURRY_CODE =
"""
import Standard.Base.Data.List.List
import Standard.Base.Data.Numbers
List.mapReverse self f acc = case self of
List.Cons h t -> @Tail_Call t.mapReverse f (List.Cons (f h) acc)
_ -> acc
main = list ->
adder = x -> y -> x + y
res = list.mapReverse (adder 1) List.Nil
res
""";
private Context context;
private Value millionElementsList;
private Value generateList;
private Value generateListQualified;
private Value reverseList;
private Value reverseListMethods;
private Value sumList;
private Value sumListLeftFold;
private Value sumListFallback;
private Value sumListMethods;
private Value mapReverseList;
private Value mapReverseListCurry;
@Setup
public void initializeBenchmarks(BenchmarkParams params) {
this.context =
Context.newBuilder()
.allowExperimentalOptions(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
var millionElemListMethod = Utils.getMainMethod(context, MILLION_ELEMENT_LIST);
this.millionElementsList = millionElemListMethod.execute();
this.generateList = Utils.getMainMethod(context, GENERATE_LIST_CODE);
this.generateListQualified = Utils.getMainMethod(context, GENERATE_LIST_QUALIFIED_CODE);
this.reverseList = Utils.getMainMethod(context, REVERSE_LIST_CODE);
this.reverseListMethods = Utils.getMainMethod(context, REVERSE_LIST_METHODS_CODE);
this.sumList = Utils.getMainMethod(context, SUM_LIST_CODE);
this.sumListLeftFold = Utils.getMainMethod(context, SUM_LIST_LEFT_FOLD_CODE);
this.sumListFallback = Utils.getMainMethod(context, SUM_LIST_FALLBACK_CODE);
this.sumListMethods = Utils.getMainMethod(context, SUM_LIST_METHODS_CODE);
this.mapReverseList = Utils.getMainMethod(context, MAP_REVERSE_LIST_CODE);
this.mapReverseListCurry = Utils.getMainMethod(context, MAP_REVERSE_LIST_CURRY_CODE);
}
@Benchmark
public void benchGenerateList(Blackhole bh) {
var res = generateList.execute(MILLION);
bh.consume(res);
}
@Benchmark
public void benchGenerateListQualified(Blackhole bh) {
var res = generateListQualified.execute(MILLION);
bh.consume(res);
}
@Benchmark
public void benchReverseList(Blackhole bh) {
var reversedList = reverseList.execute(millionElementsList);
bh.consume(reversedList);
}
@Benchmark
public void benchReverseListMethods(Blackhole bh) {
var reversedList = reverseListMethods.execute(millionElementsList);
bh.consume(reversedList);
}
@Benchmark
public void benchSumList(Blackhole bh) {
var res = sumList.execute(millionElementsList);
if (!res.fitsInLong()) {
throw new AssertionError("Should return a number");
}
bh.consume(res);
}
@Benchmark
public void sumListLeftFold(Blackhole bh) {
var res = sumListLeftFold.execute(millionElementsList);
if (!res.fitsInLong()) {
throw new AssertionError("Should return a number");
}
bh.consume(res);
}
@Benchmark
public void benchSumListFallback(Blackhole bh) {
var res = sumListFallback.execute(millionElementsList);
if (!res.fitsInLong()) {
throw new AssertionError("Should return a number");
}
bh.consume(res);
}
@Benchmark
public void benchSumListMethods(Blackhole bh) {
var res = sumListMethods.execute(millionElementsList);
if (!res.fitsInLong()) {
throw new AssertionError("Should return a number");
}
bh.consume(res);
}
@Benchmark
public void benchMapReverseList(Blackhole bh) {
var res = mapReverseList.execute(millionElementsList);
bh.consume(res);
}
@Benchmark
public void benchMapReverseCurryList(Blackhole bh) {
var res = mapReverseListCurry.execute(millionElementsList);
bh.consume(res);
}
}

View File

@ -0,0 +1,139 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.enso.interpreter.bench.Utils;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.Blackhole;
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
@Warmup(iterations = 5)
@Measurement(iterations = 5)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class CallableBenchmarks {
private static final long HUNDRED_MILLION = 100_000_000L;
private static final String SUM_TCO_FROM_CALL_CODE =
"""
from Standard.Base.Data.Numbers import all
type Foo
Foo.from (that : Number) current=0 =
if current == 0 then that else @Tail_Call Foo.from (that + current) (current - 1)
main = sumTo ->
res = Foo.from 0 sumTo
res
""";
private static final String SUM_TCO_METHOD_CALL_CODE =
"""
summator = acc -> current ->
if current == 0 then acc else @Tail_Call summator (acc + current) (current - 1)
main = sumTo ->
res = summator 0 sumTo
res
""";
private static final String SUM_TCO_METHOD_CALL_WITH_NAMED_ARGUMENTS_CODE =
"""
summator = acc -> current ->
if current == 0 then acc else @Tail_Call summator (current = current - 1) (acc = acc + current)
main = sumTo ->
res = summator current=sumTo acc=0
res
""";
private static final String SUM_TCO_METHOD_CALL_WITH_DEFAULTED_ARGUMENTS_CODE =
"""
summator = (acc = 0) -> current ->
if current == 0 then acc else @Tail_Call summator (current = current - 1) (acc = acc + current)
main = sumTo ->
res = summator current=sumTo
res
""";
private Context context;
private Value sumTCOfromCall;
private Value sumTCOmethodCall;
private Value sumTCOmethodCallWithNamedArguments;
private Value sumTCOmethodCallWithDefaultedArguments;
@Setup
public void initializeBenchmarks(BenchmarkParams params) {
this.context =
Context.newBuilder()
.allowExperimentalOptions(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
this.sumTCOfromCall = Utils.getMainMethod(context, SUM_TCO_FROM_CALL_CODE);
this.sumTCOmethodCall = Utils.getMainMethod(context, SUM_TCO_METHOD_CALL_CODE);
this.sumTCOmethodCallWithNamedArguments =
Utils.getMainMethod(context, SUM_TCO_METHOD_CALL_WITH_NAMED_ARGUMENTS_CODE);
this.sumTCOmethodCallWithDefaultedArguments =
Utils.getMainMethod(context, SUM_TCO_METHOD_CALL_WITH_DEFAULTED_ARGUMENTS_CODE);
}
@Benchmark
public void benchSumTCOfromCall(Blackhole bh) {
var res = sumTCOfromCall.execute(HUNDRED_MILLION);
if (!res.fitsInLong()) {
throw new AssertionError("Should return number");
}
bh.consume(res);
}
@Benchmark
public void benchSumTCOmethodCall(Blackhole bh) {
var res = sumTCOmethodCall.execute(HUNDRED_MILLION);
if (!res.fitsInLong()) {
throw new AssertionError("Should return number");
}
bh.consume(res);
}
@Benchmark
public void benchSumTCOmethodCallWithNamedArguments(Blackhole bh) {
var res = sumTCOmethodCallWithNamedArguments.execute(HUNDRED_MILLION);
if (!res.fitsInLong()) {
throw new AssertionError("Should return number");
}
bh.consume(res);
}
@Benchmark
public void benchSumTCOmethodCallWithDefaultedArguments(Blackhole bh) {
var res = sumTCOmethodCallWithDefaultedArguments.execute(HUNDRED_MILLION);
if (!res.fitsInLong()) {
throw new AssertionError("Should return number");
}
bh.consume(res);
}
}

View File

@ -9,7 +9,6 @@ import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.enso.interpreter.test.TestBase;
import org.enso.polyglot.MethodNames.Module;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
@ -34,7 +33,7 @@ import org.openjdk.jmh.infra.BenchmarkParams;
@Measurement(iterations = 3, time = 3)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class IfVsCaseBenchmarks extends TestBase {
public class IfVsCaseBenchmarks {
private static final int INPUT_VEC_SIZE = 100_000;
private Context ctx;
private Value ifBench3;

View File

@ -0,0 +1,94 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.enso.interpreter.bench.Utils;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.Blackhole;
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
@Warmup(iterations = 5)
@Measurement(iterations = 5)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class NamedDefaultedArgumentBenchmarks {
private static long HUNDRED_MILLION = 100_000_000L;
private static final String SUM_TCO_WITH_NAMED_ARGUMENTS_CODE =
"""
import Standard.Base.Data.Numbers
main = sumTo ->
summator = acc -> current ->
if current == 0 then acc else @Tail_Call summator (current = current - 1) (acc = acc + current)
res = summator current=sumTo acc=0
res
""";
private static final String SUM_TCO_WITH_DEFAULTED_ARGUMENTS_CODE =
"""
import Standard.Base.Data.Numbers
main = sumTo ->
summator = (acc = 0) -> current ->
if current == 0 then acc else @Tail_Call summator (current = current - 1) (acc = acc + current)
res = summator (current = sumTo)
res
""";
private Context context;
private Value sumTCOWithNamedArguments;
private Value sumTCOWithDefaultedArguments;
@Setup
public void initializeBenchmarks(BenchmarkParams params) {
this.context =
Context.newBuilder()
.allowExperimentalOptions(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
this.sumTCOWithNamedArguments = Utils.getMainMethod(context, SUM_TCO_WITH_NAMED_ARGUMENTS_CODE);
this.sumTCOWithDefaultedArguments =
Utils.getMainMethod(context, SUM_TCO_WITH_DEFAULTED_ARGUMENTS_CODE);
}
@Benchmark
public void benchSumTCOWithNamedArgs(Blackhole bh) {
var res = sumTCOWithNamedArguments.execute(HUNDRED_MILLION);
if (!res.fitsInLong()) {
throw new AssertionError("Should return number");
}
bh.consume(res);
}
@Benchmark
public void benchSumTCOWithDefaultArgs(Blackhole bh) {
var res = sumTCOWithDefaultedArguments.execute(HUNDRED_MILLION);
if (!res.fitsInLong()) {
throw new AssertionError("Should return number");
}
bh.consume(res);
}
}

View File

@ -0,0 +1,194 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.enso.interpreter.bench.Utils;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.Blackhole;
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
@Warmup(iterations = 5)
@Measurement(iterations = 5)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class RecursionBenchmarks {
private static final String SUM_TCO_CODE =
"""
main = sumTo ->
summator = acc -> current ->
if current == 0 then acc else @Tail_Call summator acc+current current-1
res = summator 0 sumTo
res
""";
private static final String SUM_TCO_FOLD_LIKE_CODE =
"""
main = sumTo ->
summator = acc -> i -> f ->
if i == 0 then acc else @Tail_Call summator (f acc i) (i - 1) f
res = summator 0 sumTo (x -> y -> x + y)
res
""";
private static final String SUM_RECURSIVE_CODE =
"""
main = sumTo ->
summator = i -> if i == 0 then 0 else i + summator (i - 1)
res = summator sumTo
res
""";
private static final String OVERSATURATED_RECURSIVE_CALL_TCO_CODE =
"""
main = sumTo ->
summator = acc -> i -> f ->
if i == 0 then acc else @Tail_Call summator (f acc i) (i - 1) f
res = summator 0 sumTo (x -> y -> x + y)
res
""";
private static final String SUM_STATE_TCO_CODE =
"""
from Standard.Base.Data.Numbers import Number
import Standard.Base.Runtime.State
stateSum = n ->
acc = State.get Number
State.put Number (acc + n)
if n == 0 then State.get Number else @Tail_Call stateSum (n - 1)
main = sumTo ->
res = State.run Number 0 (stateSum sumTo)
res
""";
private static final String SUM_TCO_WITH_EVAL_CODE =
"""
import Standard.Base.Runtime.Debug
main = sumTo ->
summator = acc -> current ->
if current == 0 then acc else Debug.eval "@Tail_Call summator (acc + current) (current - 1)"
res = summator 0 sumTo
res
""";
private static final String NESTED_THUNK_SUM_CODE =
"""
from Standard.Base.Data.Numbers import Number
import Standard.Base.Runtime.State
import Standard.Base.Nothing.Nothing
doNTimes = n -> ~block ->
block
if n == 1 then Nothing else @Tail_Call doNTimes n-1 block
main = n ->
block =
x = State.get Number
State.put Number x+1
res = State.run Number 0 (doNTimes n block)
res
""";
private static final long HUNDRED_MILLION = 100_000_000L;
private static final long HUNDRED = 100L;
private Context context;
private Value sumTCO;
private Value sumTCOWithEval;
private Value sumTCOFoldLike;
private Value sumRecursive;
private Value oversaturatedRecursiveCall;
private Value sumStateTCO;
private Value nestedThunkSum;
@Setup
public void initializeBenchmarks(BenchmarkParams params) {
this.context =
Context.newBuilder()
.allowExperimentalOptions(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
this.sumTCO = Utils.getMainMethod(context, SUM_TCO_CODE);
this.sumTCOWithEval = Utils.getMainMethod(context, SUM_TCO_WITH_EVAL_CODE);
this.sumTCOFoldLike = Utils.getMainMethod(context, SUM_TCO_FOLD_LIKE_CODE);
this.sumRecursive = Utils.getMainMethod(context, SUM_RECURSIVE_CODE);
this.oversaturatedRecursiveCall =
Utils.getMainMethod(context, OVERSATURATED_RECURSIVE_CALL_TCO_CODE);
this.sumStateTCO = Utils.getMainMethod(context, SUM_STATE_TCO_CODE);
this.nestedThunkSum = Utils.getMainMethod(context, NESTED_THUNK_SUM_CODE);
}
private Value runOnHundredMillion(Value function) {
var res = function.execute(HUNDRED_MILLION);
if (!res.fitsInLong()) {
throw new AssertionError("Should return number, but got " + res);
}
return res;
}
@Benchmark
public void benchSumTCO(Blackhole bh) {
var res = runOnHundredMillion(sumTCO);
bh.consume(res);
}
@Benchmark
public void benchSumTCOWithEval(Blackhole bh) {
var res = runOnHundredMillion(sumTCOWithEval);
bh.consume(res);
}
@Benchmark
public void benchSumTCOFoldLike(Blackhole bh) {
var res = runOnHundredMillion(sumTCOFoldLike);
bh.consume(res);
}
@Benchmark
public void benchSumRecursive(Blackhole bh) {
var res = sumRecursive.execute(HUNDRED);
if (!res.fitsInLong()) {
throw new AssertionError("Should return number, but got " + res);
}
bh.consume(res);
}
@Benchmark
public void benchOversaturatedRecursiveCall(Blackhole bh) {
var res = runOnHundredMillion(oversaturatedRecursiveCall);
bh.consume(res);
}
@Benchmark
public void benchSumStateTCO(Blackhole bh) {
var res = runOnHundredMillion(sumStateTCO);
bh.consume(res);
}
@Benchmark
public void benchNestedThunkSum(Blackhole bh) {
var res = nestedThunkSum.execute(HUNDRED_MILLION);
if (!res.isNull()) {
throw new AssertionError("Should return Nothing, but got " + res);
}
bh.consume(res);
}
}

View File

@ -1,10 +1,9 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import static org.junit.Assert.assertNotNull;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Objects;
import org.graalvm.polyglot.Source;
import org.openjdk.jmh.infra.BenchmarkParams;
@ -28,7 +27,7 @@ final class SrcUtil {
static Source read(String benchmarkName) throws IOException {
String resource = benchmarkName + ".enso";
var url = SrcUtil.class.getResource(resource);
assertNotNull("Searching for " + resource, url);
Objects.requireNonNull(url, "Searching for " + resource);
return Source.newBuilder("enso", url).name(resource).build();
}
}

View File

@ -1,15 +1,18 @@
package org.enso.interpreter.bench.benchmarks.semantic;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import org.enso.interpreter.test.TestBase;
import java.util.logging.Level;
import org.enso.polyglot.MethodNames;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
@ -29,7 +32,7 @@ import org.openjdk.jmh.infra.BenchmarkParams;
@Measurement(iterations = 3, time = 3)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class WarningBenchmarks extends TestBase {
public class WarningBenchmarks {
private static final int INPUT_VEC_SIZE = 10_000;
private static final int INPUT_DIFF_VEC_SIZE = 10_000;
private Context ctx;
@ -68,7 +71,17 @@ public class WarningBenchmarks extends TestBase {
@Setup
public void initializeBench(BenchmarkParams params) throws IOException {
ctx = createDefaultContext();
this.ctx =
Context.newBuilder()
.allowExperimentalOptions(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.allowIO(IOAccess.ALL)
.allowAllAccess(true)
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
var random = new Random(42);
benchmarkName = SrcUtil.findName(params);

View File

@ -237,6 +237,16 @@ main = Warning.attach "foo" Nothing
assertThat(nothingWithWarn.isMetaObject(), is(false));
}
@Test
public void nothingShouldBeNull() {
var src = """
import Standard.Base.Nothing.Nothing
main = Nothing
""";
var nothing = TestBase.evalModule(ctx, src);
assertThat(nothing.isNull(), is(true));
}
@Test
public void numbersAreEitherIntegerOrFloat() throws Exception {
var g = generator();

View File

@ -49,6 +49,17 @@ public class AssertionsTest extends TestBase {
out.reset();
}
@Test
public void jvmAssertionsAreEnabled() {
boolean assertsOn = false;
assert assertsOn = true;
assertTrue(
"JVM assertions must be enabled (with -ea cmd line option) in order to run all the tests"
+ " inside runtime-integration-tests project. Note that there are some features in the"
+ " runtime that work only with the JVM assertions enabled.",
assertsOn);
}
@Test
public void assertionsAreEnabled() {
EnsoContext ensoCtx =

Some files were not shown because too many files have changed in this diff Show More