Merge branch 'master' of github.com:jboner/akka

This commit is contained in:
Peter Veentjer 2011-07-15 08:18:00 +03:00
commit 4df8cb760b
3 changed files with 184 additions and 95 deletions

View file

@ -86,6 +86,7 @@ class FileBenchResultRepository extends BenchResultRepository {
}
private def save(stats: Stats) {
new File(dir).mkdirs
if (!dirExists) return
val timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date(stats.timestamp))
val name = stats.name + "--" + timestamp + "--" + stats.load + ".ser"
@ -98,8 +99,7 @@ class FileBenchResultRepository extends BenchResultRepository {
case e: Exception
EventHandler.error(this, "Failed to save [%s] to [%s], due to [%s]".
format(stats, f.getAbsolutePath, e.getMessage))
}
finally {
} finally {
if (out ne null) try { out.close() } catch { case ignore: Exception }
}
}
@ -117,8 +117,7 @@ class FileBenchResultRepository extends BenchResultRepository {
EventHandler.error(this, "Failed to load from [%s], due to [%s]".
format(f.getAbsolutePath, e.getMessage))
None
}
finally {
} finally {
if (in ne null) try { in.close() } catch { case ignore: Exception }
}
}

View file

@ -52,8 +52,7 @@ trait PerformanceTest extends JUnitSuite {
var stat: DescriptiveStatistics = _
val resultRepository = BenchResultRepository()
val legendTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
lazy val report = new Report(resultRepository, compareResultWith)
type TS <: TradingSystem
@ -128,95 +127,7 @@ trait PerformanceTest extends JUnitSuite {
resultRepository.add(stats)
EventHandler.info(this, formatResultsTable(resultRepository.get(name)))
percentilesChart(stats)
latencyAndThroughputChart(stats)
comparePercentilesChart(stats)
compareWithHistoricalPercentiliesChart(stats)
}
def percentilesChart(stats: Stats) {
val chartTitle = stats.name + " Percentiles (microseconds)"
val chartUrl = GoogleChartBuilder.percentilChartUrl(resultRepository.get(stats.name), chartTitle, _.load + " clients")
EventHandler.info(this, chartTitle + " Chart:\n" + chartUrl)
}
def comparePercentilesChart(stats: Stats) {
for {
compareName compareResultWith
compareStats resultRepository.get(compareName, stats.load)
} {
val chartTitle = stats.name + " vs. " + compareName + ", " + stats.load + " clients" + ", Percentiles (microseconds)"
val chartUrl = GoogleChartBuilder.percentilChartUrl(Seq(compareStats, stats), chartTitle, _.name)
EventHandler.info(this, chartTitle + " Chart:\n" + chartUrl)
}
}
def compareWithHistoricalPercentiliesChart(stats: Stats) {
val withHistorical = resultRepository.getWithHistorical(stats.name, stats.load)
if (withHistorical.size > 1) {
val chartTitle = stats.name + " vs. historical, " + stats.load + " clients" + ", Percentiles (microseconds)"
val chartUrl = GoogleChartBuilder.percentilChartUrl(withHistorical, chartTitle,
stats legendTimeFormat.format(new Date(stats.timestamp)))
EventHandler.info(this, chartTitle + " Chart:\n" + chartUrl)
}
}
def latencyAndThroughputChart(stats: Stats) {
val chartTitle = stats.name + " Latency (microseconds) and Throughput (TPS)"
val chartUrl = GoogleChartBuilder.latencyAndThroughputChartUrl(resultRepository.get(stats.name), chartTitle)
EventHandler.info(this, chartTitle + " Chart:\n" + chartUrl)
}
def formatResultsTable(statsSeq: Seq[Stats]): String = {
val name = statsSeq.head.name
val spaces = " "
val headerScenarioCol = ("Scenario" + spaces).take(name.length)
val headerLine = (headerScenarioCol :: "clients" :: "TPS" :: "mean" :: "5% " :: "25% " :: "50% " :: "75% " :: "95% " :: "Durat." :: "N" :: Nil)
.mkString("\t")
val headerLine2 = (spaces.take(name.length) :: " " :: " " :: "(us)" :: "(us)" :: "(us)" :: "(us)" :: "(us)" :: "(us)" :: "(s) " :: " " :: Nil)
.mkString("\t")
val line = List.fill(formatStats(statsSeq.head).replaceAll("\t", " ").length)("-").mkString
val formattedStats = "\n" +
line.replace('-', '=') + "\n" +
headerLine + "\n" +
headerLine2 + "\n" +
line + "\n" +
statsSeq.map(formatStats(_)).mkString("\n") + "\n" +
line + "\n"
formattedStats
}
def formatStats(stats: Stats): String = {
val durationS = stats.durationNanos.toDouble / 1000000000.0
val duration = durationS.formatted("%.0f")
val tpsStr = stats.tps.formatted("%.0f")
val meanStr = stats.mean.formatted("%.0f")
val summaryLine =
stats.name ::
stats.load.toString ::
tpsStr ::
meanStr ::
stats.percentiles(5).toString ::
stats.percentiles(25).toString ::
stats.percentiles(50).toString ::
stats.percentiles(75).toString ::
stats.percentiles(95).toString ::
duration ::
stats.n.toString ::
Nil
summaryLine.mkString("\t")
report.html(resultRepository.get(name))
}
def delay(delayMs: Int) {

View file

@ -0,0 +1,179 @@
package akka.performance.trading.common
import java.io.File
import java.text.SimpleDateFormat
import java.io.PrintWriter
import java.io.FileWriter
import akka.event.EventHandler
import java.util.Date
class Report(
resultRepository: BenchResultRepository,
compareResultWith: Option[String] = None) {
private val dir = System.getProperty("benchmark.resultDir", "target/benchmark")
private def dirExists: Boolean = new File(dir).exists
private def log = System.getProperty("benchmark.logResult", "false").toBoolean
val dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
val legendTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm")
val fileTimestampFormat = new SimpleDateFormat("yyyyMMddHHmmss")
def html(statistics: Seq[Stats]): Unit = if (dirExists) {
val current = statistics.last
val sb = new StringBuilder
val title = current.name + " " + dateTimeFormat.format(new Date(current.timestamp))
sb.append(header(title))
sb.append("<h1>%s</h1>\n".format(title))
sb.append("<pre>\n")
sb.append(formatResultsTable(statistics))
sb.append("\n</pre>\n")
sb.append(img(percentilesChart(current)))
sb.append(img(latencyAndThroughputChart(current)))
for (stats statistics) {
compareWithHistoricalPercentiliesChart(stats).foreach(url sb.append(img(url)))
}
for (stats statistics) {
comparePercentilesChart(stats).foreach(url sb.append(img(url)))
}
if (dirExists) {
val timestamp = fileTimestampFormat.format(new Date(current.timestamp))
val name = current.name + "--" + timestamp + ".html"
write(sb.toString, name)
}
}
private def img(url: String): String = {
"""<img src="%s" border="0" width="%s" height="%s" />""".format(
url, GoogleChartBuilder.ChartWidth, GoogleChartBuilder.ChartHeight) + "\n"
}
def percentilesChart(stats: Stats): String = {
val chartTitle = stats.name + " Percentiles (microseconds)"
val chartUrl = GoogleChartBuilder.percentilChartUrl(resultRepository.get(stats.name), chartTitle, _.load + " clients")
if (log) EventHandler.info(this, chartTitle + " Chart:\n" + chartUrl)
chartUrl
}
def comparePercentilesChart(stats: Stats): Seq[String] = {
for {
compareName compareResultWith.toSeq
compareStats resultRepository.get(compareName, stats.load)
} yield {
val chartTitle = stats.name + " vs. " + compareName + ", " + stats.load + " clients" + ", Percentiles (microseconds)"
val chartUrl = GoogleChartBuilder.percentilChartUrl(Seq(compareStats, stats), chartTitle, _.name)
if (log) EventHandler.info(this, chartTitle + " Chart:\n" + chartUrl)
chartUrl
}
}
def compareWithHistoricalPercentiliesChart(stats: Stats): Option[String] = {
val withHistorical = resultRepository.getWithHistorical(stats.name, stats.load)
if (withHistorical.size > 1) {
val chartTitle = stats.name + " vs. historical, " + stats.load + " clients" + ", Percentiles (microseconds)"
val chartUrl = GoogleChartBuilder.percentilChartUrl(withHistorical, chartTitle,
stats legendTimeFormat.format(new Date(stats.timestamp)))
if (log) EventHandler.info(this, chartTitle + " Chart:\n" + chartUrl)
Some(chartUrl)
} else {
None
}
}
def latencyAndThroughputChart(stats: Stats): String = {
val chartTitle = stats.name + " Latency (microseconds) and Throughput (TPS)"
val chartUrl = GoogleChartBuilder.latencyAndThroughputChartUrl(resultRepository.get(stats.name), chartTitle)
if (log) EventHandler.info(this, chartTitle + " Chart:\n" + chartUrl)
chartUrl
}
def formatResultsTable(statsSeq: Seq[Stats]): String = {
val name = statsSeq.head.name
val spaces = " "
val headerScenarioCol = ("Scenario" + spaces).take(name.length)
val headerLine = (headerScenarioCol :: "clients" :: "TPS" :: "mean" :: "5% " :: "25% " :: "50% " :: "75% " :: "95% " :: "Durat." :: "N" :: Nil)
.mkString("\t")
val headerLine2 = (spaces.take(name.length) :: " " :: " " :: "(us)" :: "(us)" :: "(us)" :: "(us)" :: "(us)" :: "(us)" :: "(s) " :: " " :: Nil)
.mkString("\t")
val line = List.fill(formatStats(statsSeq.head).replaceAll("\t", " ").length)("-").mkString
val formattedStats = "\n" +
line.replace('-', '=') + "\n" +
headerLine + "\n" +
headerLine2 + "\n" +
line + "\n" +
statsSeq.map(formatStats(_)).mkString("\n") + "\n" +
line + "\n"
if (log) EventHandler.info(this, formattedStats)
formattedStats
}
def formatStats(stats: Stats): String = {
val durationS = stats.durationNanos.toDouble / 1000000000.0
val duration = durationS.formatted("%.0f")
val tpsStr = stats.tps.formatted("%.0f")
val meanStr = stats.mean.formatted("%.0f")
val summaryLine =
stats.name ::
stats.load.toString ::
tpsStr ::
meanStr ::
stats.percentiles(5).toString ::
stats.percentiles(25).toString ::
stats.percentiles(50).toString ::
stats.percentiles(75).toString ::
stats.percentiles(95).toString ::
duration ::
stats.n.toString ::
Nil
summaryLine.mkString("\t")
}
def write(content: String, fileName: String) {
val f = new File(dir, fileName)
var writer: PrintWriter = null
try {
writer = new PrintWriter(new FileWriter(f))
writer.print(content)
writer.flush()
} catch {
case e: Exception
EventHandler.error(this, "Failed to save report to [%s], due to [%s]".
format(f.getAbsolutePath, e.getMessage))
} finally {
if (writer ne null) try { writer.close() } catch { case ignore: Exception }
}
}
def header(title: String) =
"""|<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|<html>
|<head>
|
|<title>%s</title>
|</head>
|<body>
|""".stripMargin.format(title)
def footer =
"""|</body>"
|</html>""".stripMargin
}