1 /// A minimal benchmarking harness. 2 module chunker.internal.benchmark; 3 4 /** 5 * To make a module benchmarkable, add: 6 * --- 7 * version (benchmarkYourModule) { import chunker.internal.benchmark; mixin BenchmarkThisModule; } 8 * --- 9 * 10 * Then, declare benchmarks by declaring private functions with a name 11 * starting with "benchmark", e.g. `void benchmarkFoo() { ... }`. 12 * 13 * To benchmark a benchmarkable module, run: 14 * --- 15 * dmd -version=benchmarkYourModule -run module.d [--N=<iterations>] [BenchmarkName ...] 16 * --- 17 */ 18 mixin template BenchmarkThisModule() 19 { 20 void main() 21 { 22 Benchmark.run(); 23 } 24 25 struct Benchmark 26 { 27 static: 28 immutable ulong iterations; 29 private immutable string[] benchmarks; 30 31 shared static this() 32 { 33 import core.runtime : Runtime; 34 import std.getopt : getopt; 35 ulong n = 1; 36 auto args = Runtime.args; 37 getopt(args, 38 "N", &n, 39 ); 40 Benchmark.iterations = n; 41 42 auto benchmarks = args[1..$].idup; 43 if (!benchmarks.length) 44 benchmarks = ["*"]; 45 Benchmark.benchmarks = benchmarks; 46 } 47 48 private void run() 49 { 50 import std.stdio : stderr; 51 import std.path : globMatch; 52 53 alias mod = __traits(parent, main); 54 foreach (name; __traits(allMembers, mod)) 55 static if (is(typeof(__traits(getMember, mod, name)))) 56 { 57 alias member = __traits(getMember, mod, name); 58 static if (__traits(isStaticFunction, member)) 59 { 60 static if (name.length > 9 && name[0..9] == "benchmark") 61 { 62 bool found = false; 63 foreach (mask; benchmarks) 64 if (globMatch(name[9..$], mask)) 65 { 66 found = true; 67 break; 68 } 69 if (!found) 70 continue; 71 72 stderr.writefln("Running benchmark %s.%s (%d iterations):", 73 __traits(identifier, mod), name[9..$], iterations); 74 best = Duration.max; 75 benchmarked = false; 76 member(); 77 assert(benchmarked, "No block was benchmarked during this benchmark."); 78 stderr.writefln(" -> %s", best); 79 } 80 } 81 } 82 } 83 84 import std.datetime.stopwatch : StopWatch; 85 import core.time : Duration; 86 87 private Duration best; 88 private bool benchmarked; 89 90 void benchmark(void delegate() dg) 91 { 92 assert(!benchmarked, "A block was already benchmarked during this benchmark."); 93 benchmarked = true; 94 95 StopWatch sw; 96 foreach (iteration; 0 .. iterations) 97 { 98 sw.start(); 99 dg(); 100 sw.stop(); 101 if (best > sw.peek()) 102 best = sw.peek(); 103 sw.reset(); 104 } 105 } 106 } 107 }