| Index: client/tests/client/samples/smoketest/BenchmarkBase.dart
|
| ===================================================================
|
| --- client/tests/client/samples/smoketest/BenchmarkBase.dart (revision 0)
|
| +++ client/tests/client/samples/smoketest/BenchmarkBase.dart (revision 0)
|
| @@ -0,0 +1,197 @@
|
| +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +/** Accessors for our Singleton variables. */
|
| +BenchmarkSuite get BENCHMARK_SUITE() {
|
| + if (BenchmarkSuite._ONLY == null) {
|
| + BenchmarkSuite._ONLY = new BenchmarkSuite._internal();
|
| + }
|
| + return BenchmarkSuite._ONLY;
|
| +}
|
| +
|
| +BenchmarkView get BENCHMARK_VIEW() {
|
| + if (BenchmarkView._ONLY == null) {
|
| + BenchmarkView._ONLY = new BenchmarkView._internal();
|
| + }
|
| + return BenchmarkView._ONLY;
|
| +}
|
| +
|
| +/** The superclass from which all benchmarks inherit from. */
|
| +class BenchmarkBase {
|
| + /** Benchmark name. */
|
| + final String name;
|
| +
|
| + 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 measures teardown code executed after the benchark runs. */
|
| + void teardown() { }
|
| +
|
| + /**
|
| + * Measures the score for this benchmark by executing it repeately until
|
| + * time minimum has been reached.
|
| + */
|
| + static double measureFor(Function f, int timeMinimum) {
|
| + int time = 0;
|
| + int iter = 0;
|
| + Stopwatch watch = new Stopwatch();
|
| + watch.start();
|
| + int elapsed = 0;
|
| + while (elapsed < timeMinimum || iter < 32) {
|
| + f();
|
| + elapsed = watch.elapsedInMs();
|
| + iter++;
|
| + }
|
| + return (1000.0 * iter) / elapsed;
|
| + }
|
| +
|
| + /**
|
| + * Measures the score for the benchmark and returns it.
|
| + * We measure iterations / sec (so bigger = better!).
|
| + */
|
| + double measure() {
|
| + setup();
|
| + // Warmup for at least 1000ms. Discard result.
|
| + measureFor(() { this.warmup(); }, 1000);
|
| + // Run the benchmark for at least 1000ms.
|
| + double result = measureFor(() { this.exercise(); }, 1000);
|
| + teardown();
|
| + return result;
|
| + }
|
| +
|
| + void report() {
|
| + num score = measure();
|
| + Map<String, int> normalizingDict = {'Smoketest': 100};
|
| + score = score / normalizingDict[name];
|
| + BENCHMARK_SUITE.updateIndividualScore(name, score);
|
| + }
|
| +}
|
| +
|
| +/** The controller class that runs all of the benchmarks. */
|
| +class BenchmarkSuite {
|
| + /** The set of benchmarks that have yet to run. */
|
| + List<Function> benchmarks;
|
| +
|
| + /**
|
| + * The set of scores from the benchmarks that have already run. (Used for
|
| + * calculating the Geometric mean).
|
| + */
|
| + List<num> scores;
|
| +
|
| + /** The total number of benchmarks we will be running. */
|
| + int totalBenchmarks;
|
| +
|
| + /** Singleton pattern: There's only one BenchmarkSuite. */
|
| + static BenchmarkSuite _ONLY = null;
|
| +
|
| + BenchmarkSuite._internal() {
|
| + scores = [];
|
| + benchmarks = [() => Smoketest.main()];
|
| + totalBenchmarks = benchmarks.length;
|
| + }
|
| +
|
| + /** Run all of the benchmarks that we have in our benchmarks list. */
|
| + runBenchmarks() {
|
| + runBenchmarksHelper(benchmarks);
|
| + }
|
| +
|
| + /**
|
| + * Run the remaining benchmarks in our list. We chain the calls providing
|
| + * little breaks for the main page to gain control, so we don't force the
|
| + * entire page to hang the whole time.
|
| + */
|
| + runBenchmarksHelper(List<Function> remainingBenchmarks) {
|
| + // Remove the last benchmark, and run it.
|
| + var benchmark = remainingBenchmarks.removeLast();
|
| + benchmark();
|
| + if (remainingBenchmarks.length > 0) {
|
| + /* Provide small breaks between each benchmark, so that the browser
|
| + doesn't get unhappy about long running scripts, and so the user
|
| + can regain control of the UI to kill the page as needed. */
|
| + window.setTimeout(() => runBenchmarksHelper(remainingBenchmarks), 25);
|
| + } else if (remainingBenchmarks.length == 0) {
|
| + // We've run all of the benchmarks. Update the page with the score.
|
| + BENCHMARK_VIEW.setScore(geometricMean(scores));
|
| + }
|
| + }
|
| +
|
| + /** Store the results of a single benchmark run. */
|
| + updateIndividualScore(String name, num score) {
|
| + scores.add(score);
|
| + BENCHMARK_VIEW.incrementProgress(name, score, totalBenchmarks);
|
| + }
|
| +
|
| + /** Computes the geometric mean of a set of numbers. */
|
| + geometricMean(numbers) {
|
| + num log = 0;
|
| + for (num n in numbers) {
|
| + log += Math.log(n);
|
| + }
|
| + return Math.pow(Math.E, log / numbers.length);
|
| + }
|
| +}
|
| +
|
| +/** Controls how results are displayed to the user, by updating the HTML. */
|
| +class BenchmarkView {
|
| +
|
| + /** The number of benchmarks that have finished executing. */
|
| + int numCompleted = 0;
|
| +
|
| + /** Singleton pattern: There's only one BenchmarkSuite. */
|
| + static BenchmarkView _ONLY = null;
|
| +
|
| + BenchmarkView._internal();
|
| +
|
| + /** Update the page HTML to show the calculated score. */
|
| + setScore(num score) {
|
| + String newScore = formatScore(score * 100.0);
|
| + Element body = document.queryAll("body")[0];
|
| + body.nodes.add(
|
| + new Element.html("<p id='testResultScore'>Score: $newScore</p>"));
|
| + }
|
| +
|
| + /**
|
| + * Update the page HTML to show how much progress we've made through the
|
| + * benchmarks.
|
| + */
|
| + incrementProgress(String name, num score, num totalBenchmarks) {
|
| + String newScore = formatScore(score * 100.0);
|
| + numCompleted++;
|
| + // Slightly incorrect (truncating) percentage, but this is just to show
|
| + // the user we're making progress.
|
| + num percentage = 100 * numCompleted ~/ totalBenchmarks;
|
| + }
|
| +
|
| + /**
|
| + * Rounds the score to have at least three significant digits (hopefully)
|
| + * helping readability of the scores.
|
| + */
|
| + String formatScore(num value) {
|
| + if (value > 100) {
|
| + return value.toStringAsFixed(0);
|
| + } else {
|
| + return value.toStringAsFixed(2);
|
| + }
|
| + }
|
| +}
|
|
|