| // 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; |
| } |