ベンチマークなモジュール

D言語で気軽に時間測定がしたい
昔書いたコードを引っ張りだしてきた.

// Written in the D programming language

/**
 * This module provides a simple benchmark utility.
 *
 * Author: $(WEB profile.livedoor.com/repeatedly/, tama)
 */

module benchmark;


import core.memory : GC;
import std.cstream : dout;
import std.perf    : PerformanceCounter;
import std.string  : ljustify;


/**
 * Benchmarks D codes based on Ruby's benchmark module.
 *
 * Example:
-----
enum Limit = 1000_000_000;

benchmark!((x) {
    x.item("for",     { for (int i; i < Limit; i++) { } });
    x.item("while",   { int i; while (i < Limit) i++;   });
    x.item("foreach", { foreach (i; 0..Limit) { }       });
});
-----
 * Result on my machine(Windows XP, Athlon 64 X2 4400+)
 *
 * Benchmark!
 * for      : 3.691605s
 * while    : 3.151104s
 * foreach  : 3.152668s
 */
class Benchmark
{
  private:
    /**
     * Represents a benchmark item
     */
    static struct Item
    {
        string caption;
        Action action;
    }

    alias void delegate() Action;

    uint               width;  // caption width
    Item[]             items;
    PerformanceCounter timer;


  public:
    this()
    {
        timer = new PerformanceCounter;
    }

    /**
     * Registers a benchmark item.
     *
     * Params:
     *  caption = display caption.
     *  action  = execute action.
     */
    void item(string caption, Action action)
    in
    {
        assert(action);
    }
    body
    {
        if (width < caption.length)
            width = caption.length + 1;

        items ~= Item(caption, action);
    }
    alias item report;

    /**
     * Measures benchmark collection.
     * Display unit is seconds.
     */
    void measure()
    {
        measure(width);
    }

    /**
     * 'measure' of $(D_PARAM width) set version.
     *
     * Params:
     *  width = user set width.
     */
    void measure(uint width)
    {
        foreach (item; items) {
            GC.collect;

            timer.start;
            item.action();
            timer.stop;

            dout.writefln(item.caption.ljustify(width) ~ ": %fs",
                          cast(real)timer.microseconds / cast(real)1000_000);
        }
    }
    alias measure exec, run;

    /**
     * Iterates through the benchmark collection.
     */
    int opApply(int delegate(ref Item) dg)
    {
        int result;

        for (uint i; i <= items.length; i++) {
            result = dg(items[i]);
            if (result)
                break;
        }

        return result;
    }
}

/**
 * Executes benchmark in makeup.
 *
 * Params:
 *  rehearsal = duplicates an benchmark if true.
 */
void benchmark(alias func)(bool rehearsal = false)
{
    auto benchmark = new Benchmark;

    func(benchmark);

    if (rehearsal) {
        dout.writefln("Rehearsal!");
        benchmark.measure;
        dout.writefln("");
        dout.flush;
    }

    dout.writefln("Benchmark!");
    benchmark.measure;
    dout.flush;
}

unittest
{
    enum Limit = 1000_000_0;

    benchmark!((x) {
        x.item("for",     { for (int i; i < Limit; i++) { } });
        x.item("while",   { int i; while (i < Limit) i++;   });
        x.item("foreach", { foreach (i; 0..Limit) { }       });
    })(true);
}

std.stdio.Fileとかが出来る前だからste.cstreamとか使ってるというね.今だと黒歴史なdoutじゃなくてstdoutだなぁ.