Skip to content

Commit

Permalink
Cross-compile the benchmarks on the JVM.
Browse files Browse the repository at this point in the history
  • Loading branch information
sjrd committed Jun 30, 2016
1 parent bb83bb4 commit cf49c2d
Show file tree
Hide file tree
Showing 19 changed files with 156 additions and 81 deletions.
57 changes: 9 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,62 +1,23 @@
# Scala.js Benchmarks

This is a port of the Dart
[benchmark harness](https://github.com/dart-lang/benchmark_harness) to
[Scala.js](https://github.com/scala-js/scala-js).

You can see the benchmarks in action
[here](http://jonas.github.io/scala-js-benchmarks/).
Benchmarks for [Scala.js](https://www.scala-js.org/)

All derivative work is the copyright of their respective authors and
distributed under their original license. All original work unless otherwise
stated is distributed under the [same license as
Scala.js](https://github.com/jonas/scala-js-benchmarks/LICENSE).
Scala.js](https://github.com/sjrd/scala-js-benchmarks/LICENSE).

## Get started

To run the benchmarks, first install the Scala.js compiler by following the
instructions in the [Scala.js README](https://github.com/lampepfl/scala-js).

Next, open `sbt` either in the root folder or in the folder of the benchmark
you want to run. Issue `packageJS` to compile the Scala code to JavaScript.
For benchmarks with a web version, open the `index-dev.html` of the in your
favorite Web browser. For benchmarks without a web version execute the
`run.sh` script inside the benchmark folder.

During development, it is useful to use `~packageJS` in sbt, so that each
time you save a source file, a compilation of the project is triggered.
Hence only a refresh of your Web page is needed to see the effects of your
changes.

## The optimized version

Instead of running `packageJS`, you can also run `optimizeJS` to generate
a much more compact version of the JavaScript code. While `index-dev.html`
refers to the JavaScript emitted by `packageJS`, `index.html` refers to the
optimized JavaScript emitted by `optimizeJS`.

The optimization phase is performed by the Advanced Optimizations of the
[Google Closure Compiler](https://developers.google.com/closure/compiler/),
which make
[strong assumptions](https://developers.google.com/closure/compiler/docs/api-tutorial3)
about the code being compiled.

All the code generated by Scala.js respects these assumptions. But if you
modify `exports.js`, make sure that you comply with them if you want to be
able to use `optimizeJS`.
Open `sbt` and run the benchmarks with the `run` command of every project.
For example,

## Troubleshooting
> richardsJS/run

### I have unresolved dependencies on `scala-js-...`
To test the fullOpt version, use, as usual:

You have probably forgotten to execute `publishLocal` from the sbt prompt in
Scala.js.
> set scalaJSStage in Global := FullOptStage

### WARNING - dangerous use of the global this object
The benchmarks cross-compiled and can be run with the JVM too:

This warning is emitted by the Google Closure Compiler when running
`optimizeJS`. The global `this` object is used in the `exports.js`
script as an alternative to `window` to export symbols used by the
various startup code. This allows the benchmarks to run both in the web
browser and in the `d8` (the V8 shell), which does not define global
objects like `window`.
> richardsJVM/run
73 changes: 44 additions & 29 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,85 +1,100 @@
import org.scalajs.core.tools.sem.CheckedBehavior.Unchecked

val projectSettings = Seq(
val projectSettings: Seq[Setting[_]] = Seq(
organization := "scalajs-benchmarks",
version := "0.1-SNAPSHOT"
)

val defaultSettings = projectSettings ++ Seq(
val defaultSettings: Seq[Setting[_]] = projectSettings ++ Seq(
scalaVersion := "2.11.8",
scalacOptions ++= Seq(
"-deprecation",
"-unchecked",
"-feature",
"-encoding", "utf8"
),
)
)

val defaultJSSettings: Seq[Setting[_]] = Seq(
scalaJSSemantics ~= { _.withAsInstanceOfs(Unchecked) },
persistLauncher := true
)

scalaJSUseRhino in Global := false

lazy val parent = project.in(file(".")).
settings(projectSettings: _*).
settings(
name := "Scala.js Benchmarks",
publishArtifact in Compile := false
).aggregate(
common,
deltablue,
richards,
sudoku,
tracer
)

lazy val common = project.
enablePlugins(ScalaJSPlugin).
lazy val common = crossProject.
settings(defaultSettings: _*).
settings(
name := s"Scala.js Benchmarks - Common",
moduleName := "common",
persistLauncher := false
name := "Scala.js Benchmarks - Common",
moduleName := "common"
)

lazy val deltablue = project.
enablePlugins(ScalaJSPlugin).
lazy val commonJVM = common.jvm
lazy val commonJS = common.js

lazy val deltablue = crossProject.crossType(CrossType.Pure).
settings(defaultSettings: _*).
jsSettings(defaultJSSettings: _*).
settings(
name := s"Scala.js Benchmarks - DeltaBlue",
name := "Scala.js Benchmarks - DeltaBlue",
moduleName := "deltablue"
).
dependsOn(common)

lazy val richards = project.
enablePlugins(ScalaJSPlugin).
lazy val deltablueJVM = deltablue.jvm
lazy val deltablueJS = deltablue.js

lazy val richards = crossProject.crossType(CrossType.Pure).
settings(defaultSettings: _*).
jsSettings(defaultJSSettings: _*).
settings(
name := s"Scala.js Benchmarks - Richards",
name := "Scala.js Benchmarks - Richards",
moduleName := "richards"
).
dependsOn(common)

lazy val sudoku = project.
enablePlugins(ScalaJSPlugin).
lazy val richardsJVM = richards.jvm
lazy val richardsJS = richards.js

lazy val sudoku = crossProject.crossType(CrossType.Pure).
settings(defaultSettings: _*).
jsSettings(defaultJSSettings: _*).
settings(
name := s"Scala.js Benchmarks - Sudoku",
name := "Scala.js Benchmarks - Sudoku",
moduleName := "sudoku"
).
dependsOn(common)

lazy val tracer = project.
enablePlugins(ScalaJSPlugin).
lazy val sudokuJVM = sudoku.jvm
lazy val sudokuJS = sudoku.js

lazy val tracer = crossProject.
settings(defaultSettings: _*).
jsSettings(defaultJSSettings: _*).
settings(
name := s"Scala.js Benchmarks - Tracer",
name := "Scala.js Benchmarks - Tracer",
moduleName := "tracer"
).
dependsOn(common)

lazy val sha512 = project.
enablePlugins(ScalaJSPlugin).
lazy val tracerJVM = tracer.jvm
lazy val tracerJS = tracer.js

lazy val sha512 = crossProject.crossType(CrossType.Pure).
settings(defaultSettings: _*).
jsSettings(defaultJSSettings: _*).
settings(
name := s"Scala.js Benchmarks - SHA-512",
name := "Scala.js Benchmarks - SHA-512",
moduleName := "sha512"
).
dependsOn(common)

lazy val sha512JVM = sha512.jvm
lazy val sha512JS = sha512.js
4 changes: 3 additions & 1 deletion common/benchmark-runner.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ detect_engines()
run_benchmark_mode()
{
engine="$1" benchmark="$2" mode="$3"
out_dir="$ROOT_DIR/$benchmark/target/scala-2.11"
test -d "$ROOT_DIR/$benchmark/.js" && \
out_dir="$ROOT_DIR/$benchmark/.js/target/scala-2.11" || \
out_dir="$ROOT_DIR/$benchmark/js/target/scala-2.11"
lib_dir="$ROOT_DIR/common"
js="$out_dir/$benchmark.$engine-$mode.js"
engine_bin=$(eval echo \$"${engine}_bin")
Expand Down
88 changes: 88 additions & 0 deletions common/jvm/src/main/scala/org/scalajs/benchmark/Benchmark.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/* __ *\
** ________ ___ / / ___ __ ____ Scala.js Benchmarks **
** / __/ __// _ | / / / _ | __ / // __/ (c) 2003-2016, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ **
** /____/\___/_/ |_/____/_/ | |__/ /____/ **
** |/____/ **
\* */

package org.scalajs.benchmark

/** Simple benchmarking framework.
*
* The `run` method has to be defined by the user, who will perform the
* timed operation there.
*
* This will run the benchmark a minimum of 5 times and for at least 2
* seconds.
*/
abstract class Benchmark {

def main(args: Array[String]): Unit = {
main()
}

def main(): Unit = {
val status = report()
println(s"$prefix: $status")
}

/** This method should be implemented by the concrete benchmark.
* It will be called by the benchmarking code for a number of times.
*
* @see setUp
* @see tearDown
*/
def run(): Unit

/** Run the benchmark the specified number of milliseconds and return
* the average execution time in microseconds.
*/
def runBenchmark(timeMinimum: Long, runsMinimum: Int): Double = {
var runs = 0
val startTime = System.nanoTime()
var stopTime = startTime + timeMinimum.toLong * 1000000L
var currentTime = startTime

do {
run()
runs += 1
currentTime = System.nanoTime()
} while (currentTime < stopTime || runs < runsMinimum)

val elapsed = currentTime - startTime
(elapsed / 1000).toDouble / runs
}

/** Prepare any data needed by the benchmark, but whose execution time
* should not be measured. This method is run before each call to the
* benchmark payload, 'run'.
*/
def setUp(): Unit = ()

/** Perform cleanup operations after each 'run'. For micro benchmarks,
* think about using the result of 'run' in a way that prevents the JVM
* to dead-code eliminate the whole 'run' method. For instance, print or
* write the results to a file. The execution time of this method is not
* measured.
*/
def tearDown(): Unit = ()

/** A string that is written at the beginning of the output line
* that contains the timings. By default, this is the class name.
*/
def prefix: String = getClass().getName()

def warmUp(): Unit = {
runBenchmark(1000, 5)
}

def report(): String = {
setUp()
warmUp()
val avg = runBenchmark(2000, 5)
tearDown()

s"$avg us"
}
}
2 changes: 1 addition & 1 deletion run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ for benchmark in $(find . -mindepth 2 -name "run.sh" | grep -vE "$EXCLUDE"); do
name="$(basename "$(dirname "$benchmark")")"
echo
info "$name [all] sbt"
TIME="%E" time sbt "$name/fastOptJS" "$name/fullOptJS" >/dev/null
TIME="%E" time sbt "${name}JS/fastOptJS" "${name}JS/fullOptJS" >/dev/null

for mode in $MODES; do
for engine in $ENGINES; do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ trait App extends org.scalajs.benchmark.BenchmarkApp {
}
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.scalajs.benchmark.dom

class CanvasRenderingContext2D {
var fillStyle: String = ""

def fillRect(x: Double, y: Double, w: Double, h: Double): Unit = ???
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.scalajs.benchmark.tracer

trait App
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package org.scalajs.benchmark.tracer

import org.scalajs.benchmark.dom._
import scala.scalajs.js

case class EngineConfiguration(
imageWidth: Int = 100,
Expand Down

0 comments on commit cf49c2d

Please sign in to comment.