This benchmarks 4 kinds of Ammonite launchers:

  • assembly (standard Java assembly / fat JAR),
  • bootstrap (lightweight coursier bootstrap, getting its classpath from the coursier cache),
  • bootstrap with isolation (lightweight coursier bootstrap, getting its classpath from the coursier cache, starting Ammonite with a two-level classloader setup),
  • hybrid with isolation (all the user-facing stuff in it like an assembly, the non-user facing stuff are embedded in it as resource JARs, and loaded via URLs like jar:…!/coursier/…/ammonite-repl.jar).

These are generated with these commands:

$ coursier bootstrap com.lihaoyi:ammonite_2.13.0:1.6.8-21-9ada29c \
    -M ammonite.Main \
    --assembly \
    -o amm-assembly
$ coursier bootstrap com.lihaoyi:ammonite_2.13.0:1.6.8-21-9ada29c \
    -M ammonite.Main \
    -o amm-bootstrap
$ coursier bootstrap com.lihaoyi:ammonite_2.13.0:1.6.8-21-9ada29c \
    -M ammonite.Main \
    --shared com.lihaoyi:ammonite-repl-api_2.13.0 \
    -o amm-bootstrap-isolation
$ coursier bootstrap com.lihaoyi:ammonite_2.13.0:1.6.8-21-9ada29c \
    -M ammonite.Main \
    --shared com.lihaoyi:ammonite-repl-api_2.13.0 \
    --hybrid \
    -o amm-hybrid-isolation

The benchmark itself consists in starting one of these launchers, and successively pasting the following 3 snippets in the console:

val b = new collection.mutable.ListBuffer[Long]()

This snippet is pasted successively 3 times:

val start = System.currentTimeMillis()
2
val end = System.currentTimeMillis()
b += end - start
val start = System.currentTimeMillis()
2
val end = System.currentTimeMillis()
b += end - start
// repeated 20 times, so that we have 20 `b += end - start`

This snippet writes results to disk (set SUFFIX in the env to adjust the output, the getOrElse is flaky):

import java.io.File.pathSeparator
import java.nio.file.Files
import java.nio.file.Paths

val suffix = sys.env.get("SUFFIX").getOrElse {
  sys.props("java.class.path")
    .split(pathSeparator)
    .head
    .stripPrefix("./")
    .stripPrefix("amm-")
}

Files.write(
  Paths.get(s"results-$suffix.txt"),
  b.mkString("\n").getBytes("UTF-8")
)

exit
In [1]:
import $ivy.`org.plotly-scala::plotly-almond:0.7.0`

import plotly._
import plotly.element._
import plotly.layout._
import plotly.Almond._
Out[1]:
import $ivy.$                                      


import plotly._

import plotly.element._

import plotly.layout._

import plotly.Almond._
In [2]:
new java.io.File("/Users/alexandre/projects/Ammonite")
  .listFiles()
  .toSeq
  .filter(_.getName.startsWith("results-"))
  .filter(_.getName.endsWith(".txt"))
  .sortBy(_.toString)
  .map { f =>
    val values = new String(java.nio.file.Files.readAllBytes(f.toPath), "UTF-8").split("\n")
      .map(_.trim)
      .filter(_.nonEmpty)
      .drop(5)
      .map(_.toLong)
    Box(x = values.toSeq, name = f.getName.stripPrefix("results-").stripSuffix(".txt"))
  }
  .plot(
    xaxis = Axis(range = (0.0, 1500.0))
  )
Out[2]:
res1: String = "plot-b303adf6-1fca-49a9-a9e0-293a521241f9"
In [3]:
// as PNG
Image.fromFile("/Users/alexandre/projects/Ammonite/newplot.png")
In [ ]: