diff --git a/.gitignore b/.gitignore index 780a2a0..fff7796 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ metals.sbt async-profiler/ diesel-samples/jvm/profile* -*gpcreds.json \ No newline at end of file +*gpcreds.json +/benchmark/jvm/*/profile.jfr diff --git a/benchmark/README.md b/benchmark/README.md new file mode 100644 index 0000000..ded8aab --- /dev/null +++ b/benchmark/README.md @@ -0,0 +1,47 @@ +# Benchmarking and Profiling + +## Scala JS + +Build in `sbt` using +``` +benchmarkJS/fastOptJS +``` +And open in [index.html](./index.html) a browser. + +Use devtool profiling ... + +## Scala JVM + +Run from `sbt` using +``` +benchmark/jmh:run -i 3 -wi 3 -f1 -t1 +benchmark/jmh:run +benchmark/jmh:run -h +``` + +### Profiling hints: +#### Java Flight Recorder +``` +benchmark/jmh:run -prof jfr:help +benchmark/jmh:run -i 2 -wi 2 -f1 -t1 -prof jfr +``` + +This will write `profile.jfr` files under _jvm/_, +which can be analyzed using IntelliJ. + + +#### async-profiler (needs to be installed separately) +``` +benchmark/jmh:run -prof async:help +benchmark/jmh:run -i 2 -wi 2 -f1 -t1 -prof async +``` + +## Adding a benchmark case + +- add a function to `diesel.benchmark.BenchmarkCases` in _shared_ sources. +- integrate with JS in _js_ sources: `diesel.benchmark.DieselBenchmark` +- integrate with JVM in _jvm_ sources: `diesel.benchmark.DieselBenchmark` + +## Links +- https://github.com/japgolly/scalajs-benchmark +- https://github.com/sbt/sbt-jmh \ No newline at end of file diff --git a/benchmark/index.html b/benchmark/index.html new file mode 100644 index 0000000..c14717f --- /dev/null +++ b/benchmark/index.html @@ -0,0 +1,12 @@ + + + + scalajs-benchmarks + + +
Loading...
+ + + + + \ No newline at end of file diff --git a/benchmark/js/src/main/scala/diesel/benchmark/DieselBenchmark.scala b/benchmark/js/src/main/scala/diesel/benchmark/DieselBenchmark.scala new file mode 100644 index 0000000..e9b0d55 --- /dev/null +++ b/benchmark/js/src/main/scala/diesel/benchmark/DieselBenchmark.scala @@ -0,0 +1,35 @@ +/* + * Copyright 2018 The Diesel Authors + * + * 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. + */ + +package diesel.benchmark + +import japgolly.scalajs.benchmark._ +import japgolly.scalajs.benchmark.gui._ + +import BenchmarkCases._ + +object DieselBenchmark { + val suite = GuiSuite( + Suite("Diesel Benchmarks")( + Benchmark("Simple BMD") { + parseSimpleBmd + }, + Benchmark("Some Glsl") { + parseSomeGlsl + } + ) + ) +} diff --git a/benchmark/js/src/main/scala/diesel/benchmark/Main.scala b/benchmark/js/src/main/scala/diesel/benchmark/Main.scala new file mode 100644 index 0000000..7fa6c52 --- /dev/null +++ b/benchmark/js/src/main/scala/diesel/benchmark/Main.scala @@ -0,0 +1,28 @@ +/* + * Copyright 2018 The Diesel Authors + * + * 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. + */ + +package diesel.benchmark + +import org.scalajs.dom.document +import japgolly.scalajs.benchmark.gui.BenchmarkGUI + +object Main { + + def main(): Unit = { + val body = document getElementById "body" + BenchmarkGUI.renderSuite(body)(DieselBenchmark.suite) + } +} diff --git a/benchmark/jvm/src/main/scala/diesel/benchmark/DieselBenchmark.scala b/benchmark/jvm/src/main/scala/diesel/benchmark/DieselBenchmark.scala new file mode 100644 index 0000000..35abe6a --- /dev/null +++ b/benchmark/jvm/src/main/scala/diesel/benchmark/DieselBenchmark.scala @@ -0,0 +1,33 @@ +/* + * Copyright 2018 The Diesel Authors + * + * 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. + */ + +package diesel.benchmark + +import diesel.benchmark.BenchmarkCases._ +import org.openjdk.jmh.annotations.Benchmark + +class DieselBenchmark { + @Benchmark + def simpleBMD(): Unit = { + parseSimpleBmd + } + + @Benchmark + def someGlsl(): Unit = { + parseSomeGlsl + } + +} diff --git a/benchmark/shared/src/main/scala/diesel/benchmark/BenchmarkCases.scala b/benchmark/shared/src/main/scala/diesel/benchmark/BenchmarkCases.scala new file mode 100644 index 0000000..cf9843e --- /dev/null +++ b/benchmark/shared/src/main/scala/diesel/benchmark/BenchmarkCases.scala @@ -0,0 +1,149 @@ +/* + * Copyright 2018 The Diesel Authors + * + * 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. + */ + +package diesel.benchmark + +import diesel.samples.glsl.Glsl +import diesel.samples.jsmodeldsl.BmdDsl +import diesel.{AstHelpers, Dsl} + +object BenchmarkCases { + + def parseSimpleBmd: Unit = { + val text = + """|start with a Foo. + |a Foo is a concept. + |a Gnu is a xx. + |""".stripMargin + parseSome(BmdDsl, Some(BmdDsl.aCompileUnit))(text) + } + + def parseSomeGlsl: Unit = { + val text = + """void mainImage( out vec4 fragColor, in vec2 fragCoord ) + |{ + | vec2 uv = fragCoord/iResolution.xy; + | + | if (true) { + | return 12; + | } + | + | vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); + | + | fragColor = vec4(col,1.0); + |} + |void mainImage( out vec4 fragColor, in vec2 fragCoord ) + |{ + | vec2 uv = fragCoord/iResolution.xy; + | + | if (true) { + | return 12; + | } + | + | vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); + | + | fragColor = vec4(col,1.0); + |} + |void mainImage( out vec4 fragColor, in vec2 fragCoord ) + |{ + | vec2 uv = fragCoord/iResolution.xy; + | + | if (true) { + | return 12; + | } + | + | vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); + | + | fragColor = vec4(col,1.0); + |} + |void mainImage( out vec4 fragColor, in vec2 fragCoord ) + |{ + | vec2 uv = fragCoord/iResolution.xy; + | + | if (true) { + | return 12; + | } + | + | vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); + | + | fragColor = vec4(col,1.0); + |} + |void mainImage( out vec4 fragColor, in vec2 fragCoord ) + |{ + | vec2 uv = fragCoord/iResolution.xy; + | + | if (true) { + | return 12; + | } + | + | vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); + | + | fragColor = vec4(col,1.0); + |} + |void mainImage( out vec4 fragColor, in vec2 fragCoord ) + |{ + | vec2 uv = fragCoord/iResolution.xy; + | + | if (true) { + | return 12; + | } + | + | vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); + | + | fragColor = vec4(col,1.0); + |} + | + |float x = 12; + | + |void mainImage( out vec4 fragColor, in vec2 fragCoord ) + |{ + | vec2 uv = fragCoord/iResolution.xy; + | + | if (true) { + | return 12; + | } + | + | vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); + | + | fragColor = vec4(col,1.0); + |} + | + |void mainImage( out vec4 fragColor, in vec2 fragCoord ) + |{ + | vec2 uv = fragCoord/iResolution.xy; + | + | if (true) { + | return 12; + | } + | + | vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); + | + | fragColor = vec4(col,1.0); + |} + |""".stripMargin + parseSome(Glsl, Some(Glsl.a))(text) + } + + private def parseSome(dsl: Dsl, axiom: Option[Dsl.Axiom[_]] = None)(text: String): Unit = { + AstHelpers.parse(dsl, text, axiom) + } + + // use main with IntelliJ profiler + def main(args: Array[String]): Unit = { + parseSimpleBmd + parseSimpleBmd + } +} diff --git a/build.sbt b/build.sbt index 85f2b6a..166c5d4 100644 --- a/build.sbt +++ b/build.sbt @@ -1,7 +1,6 @@ import sbt.{CrossVersion, ThisBuild} import sbtcrossproject.CrossPlugin.autoImport.crossProject - -import scala.sys.process._ +import org.scalajs.linker.interface.ModuleInitializer.mainMethod val scalaVersion2 = "2.13.10" // val scalaVersion3 = "3.2.1" @@ -44,7 +43,15 @@ addCommandAlias("testJS", "all dieselJS/test samplesJS/test") lazy val root = project .in(file(".")) - .aggregate(diesel.jvm, diesel.js, samples.jvm, samples.js, samplesBundle) + .aggregate( + diesel.jvm, + diesel.js, + samples.jvm, + samples.js, + samplesBundle, + benchmark.jvm, + benchmark.js + ) .settings(commonSettings) .settings(sonatypeSettings) .settings(copyrightSettings) @@ -142,3 +149,27 @@ lazy val samplesBundle = project .settings(commonSettings) .settings(copyrightSettings) .dependsOn(samples.js) + +lazy val benchmark = crossProject(JSPlatform, JVMPlatform) + .withoutSuffixFor(JVMPlatform) + .settings(commonSettings) + .settings(copyrightSettings) + .settings( + name := "diesel-core-benchmark", + publish / skip := true, + test := {} + ) + .settings(sharedSettings_scalac) + .settings(sharedSettings_lint) + .enablePlugins(JSDependenciesPlugin, JmhPlugin) + .jsSettings( + libraryDependencies ++= Seq( + "com.github.japgolly.scalajs-benchmark" %%% "benchmark" % "0.10.0", + "org.scala-js" %%% "scalajs-dom" % "2.5.0" + ), + scalaJSUseMainModuleInitializer := true, + Compile / scalaJSMainModuleInitializer := Some(mainMethod("diesel.benchmark.Main", "main")), + scalaJSLinkerConfig ~= { _.withSourceMap(true) }, + packageJSDependencies / skip := false + ) + .dependsOn(samples) diff --git a/project/plugins.sbt b/project/plugins.sbt index db7a159..903ef35 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,9 +1,11 @@ addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.1") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.13.1") +addSbtPlugin("org.scala-js" % "sbt-jsdependencies" % "1.0.2") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.0") addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.10.4") addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.9.0") addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.12") +addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.4") addSbtPlugin( "com.ibm.cloud.diesel" % "diesel-i18n-plugin" % "0.6.0"