blob: 0de16db745f303e0e4b830a1b4addb657c81f77e [file] [log] [blame]
// Copyright 2011 Google Inc. All Rights Reserved.
library benchmark_base;
import "dart:math" as Math;
class Expect {
static void equals(var expected, var actual) {
if (expected != actual) {
throw "Values not equal: $expected vs $actual";
}
}
static void listEquals(List expected, List actual) {
if (expected.length != actual.length) {
throw "Lists have different lengths: ${expected.length} vs ${actual.length}";
}
for (int i = 0; i < actual.length; i++) {
equals(expected[i], actual[i]);
}
}
fail(message) {
throw message;
}
}
const double MILLIS_PER_SECOND = 1e3;
const double MICROS_PER_SECOND = 1e6;
typedef void BenchmarkBody();
class BenchmarkBase {
final String name;
// Empty constructor.
const BenchmarkBase(String name) : this.name = name;
// The benchmark code.
// This function is not used, if both [warmup] and [exercise] are overwritten.
void run() {}
// Runs a short version of the benchmark. By default invokes [run] once.
void warmup() {
run();
}
// Exercices the benchmark. By default invokes [run] 10 times.
void exercise() {
for (int i = 0; i < 10; i++) {
run();
}
}
// Not measured setup code executed prior to the benchmark runs.
void setup() {}
// Not measured teardown code executed after the benchmark runs.
void teardown() {}
// Measures the score for this benchmark by executing it repeatedly until
// millisMinimum milliseconds has been reached.
static double measureFor(BenchmarkBody f, int millisMinimum) {
int iter = 0;
int elapsed = 0;
Stopwatch watch = new Stopwatch();
// StopWatch.frequency is in Hz.
double secondsMinimum = millisMinimum / MILLIS_PER_SECOND;
int ticksMinimum = (secondsMinimum * watch.frequency).ceil();
watch.start();
while (elapsed < ticksMinimum) {
f();
elapsed = watch.elapsedTicks;
iter++;
}
double totalSeconds = elapsed / watch.frequency;
return (totalSeconds / iter) * MICROS_PER_SECOND;
}
// Measures the score for the benchmark and returns it.
double measure() {
setup();
// Warmup for at least 100ms. Discard result.
measureFor(() {
// We have to ensure that v8 does not inline the outer
// measurement loop to make sure that we benchmark the
// right thing.
// See https://code.google.com/p/v8/issues/detail?id=3354
var useless = r"""
.___ __ .__ .__ .__
__| _/____ ____ _____/ |_ |__| ____ | | |__| ____ ____
/ __ |/ _ \ / \ / _ \ __\ | |/ \| | | |/ \_/ __ \
/ /_/ ( <_> ) | | ( <_> ) | | | | \ |_| | | \ ___/
\____ |\____/ |___| /\____/|__| |__|___| /____/__|___| /\___ >
\/ \/ \/ \/ \/
__ .__ .__ _____ __ .__
_/ |_| |__ |__| ______ _/ ____\_ __ ____ _____/ |_|__| ____ ____
\ __\ | \| |/ ___/ \ __\ | \/ \_/ ___\ __\ |/ _ \ / \
| | | Y \ |\___ \ | | | | / | \ \___| | | ( <_> ) | \
|__| |___| /__/____ > |__| |____/|___| /\___ >__| |__|\____/|___| /
\/ \/ \/ \/ \/
""";
((x) => x)(useless);
this.warmup();
}, 100);
// Run the benchmark for at least 2000ms.
double result = measureFor(() {
// We have to ensure that v8 does not inline the outer
// measurement loop to make sure that we benchmark the
// right thing.
// See https://code.google.com/p/v8/issues/detail?id=3354
var useless = r"""
.___ __ .__ .__ .__
__| _/____ ____ _____/ |_ |__| ____ | | |__| ____ ____
/ __ |/ _ \ / \ / _ \ __\ | |/ \| | | |/ \_/ __ \
/ /_/ ( <_> ) | | ( <_> ) | | | | \ |_| | | \ ___/
\____ |\____/ |___| /\____/|__| |__|___| /____/__|___| /\___ >
\/ \/ \/ \/ \/
__ .__ .__ _____ __ .__
_/ |_| |__ |__| ______ _/ ____\_ __ ____ _____/ |_|__| ____ ____
\ __\ | \| |/ ___/ \ __\ | \/ \_/ ___\ __\ |/ _ \ / \
| | | Y \ |\___ \ | | | | / | \ \___| | | ( <_> ) | \
|__| |___| /__/____ > |__| |____/|___| /\___ >__| |__|\____/|___| /
\/ \/ \/ \/ \/
""";
((x) => x)(useless);
this.exercise();
}, 2000);
teardown();
return result;
}
void report() {
double score = measure();
print("$name(RunTime): $score us.");
}
// A part of the Benchmark harness that runs a test for a fixed number of
// iterations. Keeping the work constant across all runs.
// Measures the score for this benchmark by executing it repeatedly for
// a fixed number of iterations.
static double measureForIterations(BenchmarkBody f, int iterations) {
Stopwatch watch = new Stopwatch();
watch.start();
for (var i = 0; i < iterations; i++) {
f();
}
watch.stop();
double totalSeconds = watch.elapsedTicks / watch.frequency;
return (totalSeconds / iterations) * MICROS_PER_SECOND;
}
double measureIterations(int iterations) {
setup();
// Warmup for 1/10th iterations. Discard result.
measureForIterations(() {
this.warmup();
}, Math.max(1, iterations ~/ 10));
// Run the benchmark for a fixed number of iterations.
double result = measureForIterations(() {
this.exercise();
}, iterations);
teardown();
return result;
}
void reportIterations(int iterations) {
double score = measureIterations(iterations);
print("$name(RunTime): $score us.");
}
}
// Fixed seed predictable random number generator that matches
// one used in the Octane suite.
class OctaneRandom implements Math.Random {
var seed = 49734321;
nextDouble() {
// Robert Jenkins' 32 bit integer hash function.
seed = ((seed + 0x7ed55d16) + (seed << 12)) & 0xffffffff;
seed = ((seed ^ 0xc761c23c) ^ (seed >> 19)) & 0xffffffff;
seed = ((seed + 0x165667b1) + (seed << 5)) & 0xffffffff;
seed = ((seed + 0xd3a2646c) ^ (seed << 9)) & 0xffffffff;
seed = ((seed + 0xfd7046c5) + (seed << 3)) & 0xffffffff;
seed = ((seed ^ 0xb55a4f09) ^ (seed >> 16)) & 0xffffffff;
return (seed & 0xfffffff) / 0x10000000;
}
nextInt(max) => (nextDouble() * max).floor();
nextBool() => nextDouble() < 0.5;
}