Skip to content

Commit

Permalink
Mainly refactoring.
Browse files Browse the repository at this point in the history
  • Loading branch information
Aleksandar Prokopec committed Oct 9, 2012
1 parent 7d698ea commit b9e3a6a
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 69 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
target
project/target
tmp

tmp2
2 changes: 1 addition & 1 deletion src/main/scala/org/collperf/Executor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import compat.Platform

trait Executor {

def run[T](benchmark: Setup[T]): CurveData
def run[T](setups: Seq[Setup[T]]): Seq[CurveData]

}

Expand Down
21 changes: 11 additions & 10 deletions src/main/scala/org/collperf/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ object Main {
// prepare top-level context
// identify test objects
// create reporters and persistors
configurationContext = Context.machine ++ configuration.context + (Key.persistor -> configuration.persistor)
configurationContext = Context.machine ++ configuration.context
currentContext.value = configurationContext
import configuration._

Expand All @@ -29,18 +29,19 @@ object Main {
}
}

case class Configuration(benches: Seq[String], persistor: Persistor, context: Context)
case class Configuration(benches: Seq[String], context: Context)

object Configuration extends JavaTokenParsers {

private def persistorFor(name: String) = name match {
case "None" => Persistor.None
case "Serialization" => new persistance.SerializationPersistor
}

def fromCommandLineArgs(args: Array[String]) = {
def arguments: Parser[Configuration] = rep(arg) ^^ {
case configs => configs.foldLeft(Configuration(Nil, Persistor.None, Context.empty)) {
case (acc, x) => Configuration(acc.benches ++ x.benches, x.persistor, acc.context ++ x.context)
case configs => configs.foldLeft(Configuration(Nil, Context.empty)) {
case (acc, x) => Configuration(acc.benches ++ x.benches, acc.context ++ x.context)
}
}
def arg: Parser[Configuration] = benches | persistor | intsetting | stringsetting | flag
Expand All @@ -50,20 +51,20 @@ object Main {
def classnames: Parser[Seq[String]] = repsep(classname, ":")
def classname: Parser[String] = repsep(ident, ".") ^^ { _.mkString(".") }
def benches: Parser[Configuration] = listOf("benches", "b") ^^ {
case names => Configuration(names, Persistor.None, Context.empty)
case names => Configuration(names, Context.empty)
}
def persistor: Parser[Configuration] = "-" ~ ("persistor" | "p") ~ ident ^^ {
case _ ~ _ ~ name => Configuration(Nil, persistorFor(name), Context.empty)
case _ ~ _ ~ name => Configuration(Nil, Context(Key.persistor -> persistorFor(name)))
}
def intsetting: Parser[Configuration] = "-" ~ ident ~ decimalNumber ^^ {
case _ ~ "Cwarmups" ~ num => Configuration(Nil, Persistor.None, Context(Key.warmupRuns -> num.toInt))
case _ ~ "Cruns" ~ num => Configuration(Nil, Persistor.None, Context(Key.benchRuns -> num.toInt))
case _ ~ "Cwarmups" ~ num => Configuration(Nil, Context(Key.warmupRuns -> num.toInt))
case _ ~ "Cruns" ~ num => Configuration(Nil, Context(Key.benchRuns -> num.toInt))
}
def stringsetting: Parser[Configuration] = "-" ~ ident ~ ident ^^ {
case _ ~ "Cresultdir" ~ s => Configuration(Nil, Persistor.None, Context(Key.resultDir -> s))
case _ ~ "Cresultdir" ~ s => Configuration(Nil, Context(Key.resultDir -> s))
}
def flag: Parser[Configuration] = "-" ~ ident ^^ {
case _ ~ "verbose" => Configuration(Nil, Persistor.None, Context(Key.verbose -> true))
case _ ~ "verbose" => Configuration(Nil, Context(Key.verbose -> true))
case _ ~ unknownFlag => sys.error(s"Unknown flag '$unknownFlag'")
}

Expand Down
24 changes: 17 additions & 7 deletions src/main/scala/org/collperf/dsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import collection._



trait DSL {
trait DSL extends DelayedInit {

private val curves = new scala.util.DynamicVariable(List[CurveData]())
private val curves = new scala.util.DynamicVariable(mutable.ArrayBuffer[CurveData]())
private val setups = new scala.util.DynamicVariable(mutable.ArrayBuffer[Setup[_]]())

def executor: Executor

Expand All @@ -20,17 +21,22 @@ trait DSL {
block

val persistor = currentContext.value.goe(Key.persistor, Persistor.None)
val cs = curves.value.reverse
val cs = curves.value
reporter.report(ResultData(cs, cs.head.context), persistor)
curves.value = Nil
curves.value.clear()
}
}
}

type SameType

object measure {
def method(methodname: String) = new {
def in(block: =>Unit): Unit = currentContext.withAttribute(Key.method, methodname) {
block

curves.value ++= executor.run(setups.value.asInstanceOf[Seq[Setup[SameType]]])
setups.value.clear()
}
}
}
Expand All @@ -41,12 +47,16 @@ trait DSL {
def warmUp(block: =>Any) = Using(benchmark.copy(customwarmup = Some(() => block)))
def curve(name: String) = Using(benchmark.copy(context = benchmark.context + (Key.curve -> name)))
def apply(block: T => Any) {
val curve = benchmark.copy(snippet = block).run()
curves.value ::= curve
setups.value += benchmark.copy(snippet = block)
}
}

def using[T](gen: Gen[T]) = Using(Setup(executor, reporter, currentContext.value, gen, None, None, None, null))
def using[T](gen: Gen[T]) = Using(Setup(currentContext.value, gen, None, None, None, null))

def delayedInit(body: =>Unit) {
// TODO refactor dsl to make a tree, run all warmups
body
}

}

Expand Down
61 changes: 44 additions & 17 deletions src/main/scala/org/collperf/execution/LocalExecutor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,48 +27,65 @@ import compat.Platform
*/
class LocalExecutor(val aggregator: Aggregator) extends Executor {

case class Warmer(warmups: Int, set: () => Any, tear: () => Any) {
case class Warmer(maxwarmups: Int, setup: () => Any, teardown: () => Any) {
def foreach[U](f: Int => U): Unit = {
val withgc = new utils.SlidingWindow(10)
val withoutgc = new utils.SlidingWindow(10)
@volatile var nogc = true

log.verbose(s"Starting warmup.")

utils.withGCNotification { n =>
log.verbose("Garbage collection detected.")
nogc = false
log.verbose("Garbage collection detected.")
} apply {
set()
setup()
var i = 0
while (i < warmups) {
while (i < maxwarmups) {
nogc = true

val start = Platform.currentTime
f(i)
val end = Platform.currentTime
val runningtime = end - start

if (nogc) withoutgc.add(runningtime)
withgc.add(runningtime)
tear()
set()
teardown()
setup()

val covNoGC = withoutgc.cov
val covGC = withgc.cov

nogc = true
log.verbose(s"$i. warmup run running time: $runningtime (covNoGC: $covNoGC, covGC: $covGC)")
if ((withoutgc.size >= 10 && covNoGC < 0.1) || (withgc.size >= 10 && covGC < 0.1)) {
log.verbose(s"Steady-state detected.")
i = warmups
i = maxwarmups
} else i += 1
}
log.verbose(s"Ending warmup.")
}
}
}

def run[T](benchmark: Setup[T]): CurveData = {
import benchmark._
def run[T](setups: Seq[Setup[T]]) = {
// run all warmups for classloading purposes
for (bench <- setups) {
import bench._
for (x <- gen.warmupset) {
val warmups = context.goe(Key.warmupRuns, 1)
for (_ <- Warmer(warmups, setupFor(x), teardownFor(x))) snippet(x)
}
}

val set = setup.orNull
val tear = teardown.orNull
// for every benchmark - do a warmup, and then measure
for (bench <- setups) yield {
runSingle(bench)
}
}

private def runSingle[T](benchmark: Setup[T]): CurveData = {
import benchmark._

// run warm up
val warmups = context.goe(Key.warmupRuns, 1)
Expand All @@ -77,9 +94,7 @@ class LocalExecutor(val aggregator: Aggregator) extends Executor {
for (i <- 0 until warmups) warmup()
case _ =>
for (x <- gen.warmupset) {
val s = () => if (set != null) set(x)
val t = () => if (tear != null) tear(x)
for (i <- Warmer(warmups, s, t)) snippet(x)
for (i <- Warmer(warmups, setupFor(x), teardownFor(x))) snippet(x)
}
}

Expand All @@ -90,17 +105,19 @@ class LocalExecutor(val aggregator: Aggregator) extends Executor {
val measurements = new mutable.ArrayBuffer[Measurement]()
val repetitions = context.goe(Key.benchRuns, 1)
for ((x, params) <- gen.dataset) {
val set = setupFor(x)
val tear = teardownFor(x)
var iteration = 0
var times = List[Long]()
while (iteration < repetitions) {
if (set != null) set(x)
set()

val start = Platform.currentTime
snippet(x)
val end = Platform.currentTime
val time = end - start

if (tear != null) tear(x)
tear()

times ::= time
iteration += 1
Expand All @@ -124,3 +141,13 @@ object LocalExecutor {
}












2 changes: 1 addition & 1 deletion src/main/scala/org/collperf/execution/NewJvmExecutor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import collection._

object NewJVMExecutor extends Executor {

def run[T](benchmark: Setup[T]): CurveData = {
def run[T](benchmark: Seq[Setup[T]]): Seq[CurveData] = {
// TODO

null
Expand Down
11 changes: 9 additions & 2 deletions src/main/scala/org/collperf/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,9 @@ package collperf {

case class History(results: Seq[(Date, ResultData)])

case class Setup[T](executor: Executor, reporter: Reporter, context: Context, gen: Gen[T], setup: Option[T => Any], teardown: Option[T => Any], customwarmup: Option[() => Any], snippet: T => Any) {
def run(): CurveData = executor.run(this)
case class Setup[T](context: Context, gen: Gen[T], setup: Option[T => Any], teardown: Option[T => Any], customwarmup: Option[() => Any], snippet: T => Any) {
def setupFor(v: T) = if (setup.isEmpty) { () => } else { () => setup.get(v) }
def teardownFor(v: T) = if (teardown.isEmpty) { () => } else { () => teardown.get(v) }
}

case class Statistic(min: Long, max: Long, average: Long, stdev: Long, median: Long)
Expand Down Expand Up @@ -180,6 +181,12 @@ package collperf {
))
}

def complete(a: Aggregator) = new Aggregator {
def name = a.name
def apply(times: Seq[Long]) = a(times)
def data(times: Seq[Long]) = Some(times)
}

def withData(a: Aggregator)(as: Aggregator*) = new Aggregator {
def name = a.name
def apply(times: Seq[Long]) = a(times)
Expand Down
34 changes: 24 additions & 10 deletions src/main/scala/org/collperf/reporting/HtmlReporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import xml._

case class HtmlReporter(val renderers: HtmlReporter.Renderer*) extends Reporter {

val sep = File.separator

def head =
<head>
<title>Performance report</title>
Expand All @@ -29,7 +31,8 @@ case class HtmlReporter(val renderers: HtmlReporter.Renderer*) extends Reporter
for ((method, methodresults) <- moduleresults.orderedGroupBy(_.context.goe(Key.method, ""))) yield <p><div>
<h3>Method: {method}</h3>
{
for (r <- renderers) yield r.render(methodresults)
val history = persistor.load(methodresults.head.context)
for (r <- renderers) yield r.render(methodresults, history)
}
</div></p>
}
Expand All @@ -52,15 +55,17 @@ case class HtmlReporter(val renderers: HtmlReporter.Renderer*) extends Reporter
def report(results: ResultData, persistor: Persistor) {
val resultdir = results.context.goe(Key.resultDir, "tmp")

new File(s"$resultdir/report/images").mkdir()
new File(s"$resultdir/report/lib").mkdir()
new File(s"$resultdir").mkdir()
new File(s"$resultdir${sep}report").mkdir()
new File(s"$resultdir${sep}report${sep}images").mkdir()
new File(s"$resultdir${sep}report${sep}lib").mkdir()

val report = <html>{head ++ body(results, persistor)}</html>

val css = getClass.getClassLoader.getResourceAsStream("css/index.css")
try {
val reader = new BufferedReader(new InputStreamReader(css))
printToFile(new File(s"$resultdir/report/lib/index.css")) { p =>
printToFile(new File(s"$resultdir${sep}report${sep}lib${sep}index.css")) { p =>
var line = ""
while (line != null) {
p.println(line)
Expand All @@ -71,7 +76,7 @@ case class HtmlReporter(val renderers: HtmlReporter.Renderer*) extends Reporter
css.close()
}

printToFile(new File(s"$resultdir/report/index.html")) {
printToFile(new File(s"$resultdir${sep}report${sep}index.html")) {
_.println(report.toString)
}
}
Expand All @@ -87,14 +92,14 @@ case class HtmlReporter(val renderers: HtmlReporter.Renderer*) extends Reporter
object HtmlReporter {

trait Renderer {
def render(curves: Seq[CurveData]): Node
def render(curves: Seq[CurveData], h: History): Node
}

object Renderer {
def all = Seq(Info(), BigO(), Chart(ChartReporter.ChartFactory.XYLine()))

case class Info() extends Renderer {
def render(curves: Seq[CurveData]): Node =
def render(curves: Seq[CurveData], h: History): Node =
<div>Info:
<ul>
<li>Number of runs: {curves.head.context.goe(Key.benchRuns, "")}</li>
Expand All @@ -104,7 +109,7 @@ object HtmlReporter {
}

case class BigO() extends Renderer {
def render(curves: Seq[CurveData]): Node =
def render(curves: Seq[CurveData], h: History): Node =
<div>Big O analysis:
<ul>
{
Expand All @@ -117,11 +122,11 @@ object HtmlReporter {
}

case class Chart(factory: ChartReporter.ChartFactory) extends Renderer {
def render(curves: Seq[CurveData]): Node = {
def render(curves: Seq[CurveData], h: History): Node = {
val resultdir = curves.head.context.goe(Key.resultDir, "tmp")
val scopename = curves.head.context.scopeName
val chart = factory.createChart(scopename, curves)
val chartfile = new File(s"$resultdir/report/images/$scopename.png")
val chartfile = new File(s"$resultdir${File.separator}report${File.separator}images${File.separator}$scopename.png")
ChartUtilities.saveChartAsPNG(chartfile, chart, 1600, 1200)

<div>
Expand All @@ -131,6 +136,15 @@ object HtmlReporter {
</div>
}
}

case class Regression() extends Renderer {
def render(curves: Seq[CurveData], h: History): Node = {
// TODO

<div>History:
</div>
}
}
}

}
Expand Down
Loading

0 comments on commit b9e3a6a

Please sign in to comment.