All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scalameter.execution.SeparateJvmsExecutor.scala Maven / Gradle / Ivy

The newest version!
package org.scalameter
package execution



import org.scalameter.picklers.Pickler
import scala.collection._
import scala.util.Try
import org.scalameter.PrettyPrinter.Implicits._



/** Runs multiple JVM instances per each setup and aggregates all the results together.
 *
 *  This produces more stable results, as the performance related effects of each JVM instantiation
 *  are averaged.
 */
class SeparateJvmsExecutor[V: Pickler: PrettyPrinter](val warmer: Warmer, val aggregator: Aggregator[V],
  val measurer: Measurer[V]) extends Executor[V] {

  import Key._

  val runner = new JvmRunner

  def createJvmContext(ctx: Context) = {
    val existingFlags = ctx(exec.jvmflags)
    val flags = if (currentContext(Key.verbose)) "-verbose:gc" :: existingFlags else existingFlags
    ctx + (exec.jvmflags -> flags)
  }

  def runSetup[T](setup: Setup[T]): CurveData[V] = {
    import setup._

    val warmups = context(exec.maxWarmupRuns)
    val totalreps = context(exec.benchRuns)
    val independentSamples = context(exec.independentSamples)
    def repetitions(idx: Int): Int = {
      val is = independentSamples
      totalreps / is + (if (idx < totalreps % is) 1 else 0)
    }

    val m = measurer
    val w = warmer
    val jvmContext = createJvmContext(context)

    def sample(idx: Int, reps: Int): Try[Seq[(Parameters, Seq[(V, String)])]] = runner.run(jvmContext) {
      dyn.currentContext.value = context
      
      log.verbose(s"Sampling $reps measurements in separate JVM invocation $idx - ${context.scope}, ${context(dsl.curve)}.")

      // warmup
      setupBeforeAll()
      try {
        customwarmup match {
          case Some(warmup) =>
            for (i <- 0 until warmups) warmup()
          case _ =>
            for (x <- gen.warmupset) {
              for (i <- w.warming(context, setupFor(x), teardownFor(x))) snippet(x)
            }
        }

        // perform GC
        compat.Platform.collectGarbage()

        // measure
        val observations = new mutable.ArrayBuffer[(Parameters, Seq[(V, String)])]()
        for (params <- gen.dataset) {
          val set = setupFor()
          val tear = teardownFor()
          val regen = regenerateFor(params)
          val results = m.measure(context, reps, set, tear, regen, snippet)
          observations += ((params, results.map(q => q.value -> q.units))) // FIXME: workaround to `java.lang.ClassNotFoundException: org.scalameter.Quantity`
        }
        observations
      } finally {
        teardownAfterAll()
      }
    }

    def sampleReport(idx: Int, reps: Int): Seq[(Parameters, Seq[Quantity[V]])] = try {
      sample(idx, reps).get.map(v => (v._1, v._2.map(v => Quantity(v._1, v._2))))
    } catch {
      case t: Throwable =>
        log.error(s"Error running separate JVM: $t")
        log.error(s"Classpath: ${sys.props("java.class.path")}")
        throw t
    }

    log.verbose(s"Running test set for ${context.scope}, curve ${context(dsl.curve)}")
    log.verbose(s"Starting $totalreps measurements across $independentSamples independent JVM runs.")

    val valueseqs = for {
      idx <- 0 until independentSamples
      reps = repetitions(idx)
    } yield sampleReport(idx, reps)

    val valueseq = valueseqs.reduceLeft { (accseq, vs) =>
      accseq zip vs map {
        case ((k1, x), (k2, y)) => (k1, x ++ y)
      }
    }

    def nice(vs: Seq[(Parameters, Seq[Quantity[V]])]) = vs map { case (params, seq) =>
      params.axisData.mkString(", ") + ": " + seq.map(t => s"${t.value.prettyPrint}").mkString(", ")
    } mkString("\n")

    log.verbose(s"Obtained measurements:\n${nice(valueseq)}")

    val measurements = valueseq map {
      case (params, values) =>
        val single = aggregator(values)
        Measurement(
          single.value,
          params,
          aggregator.data(values),
          single.units
        )
    }

    CurveData(measurements.toSeq, Map.empty, context)
  }

  override def toString = s"MultipleJvmPerSetupExecutor(${aggregator.name}, ${measurer.name})"

}


object SeparateJvmsExecutor extends Executor.Factory[SeparateJvmsExecutor] {

  def apply[T: Pickler: PrettyPrinter](w: Warmer, agg: Aggregator[T], m: Measurer[T]) =
    new SeparateJvmsExecutor(w, agg, m)

}
































© 2015 - 2025 Weber Informatics LLC | Privacy Policy