1 module testquark.testquark; 2 import std.stdio; 3 import std.json; 4 import std.algorithm; 5 import std.traits; 6 import std.array; 7 import std.math; 8 9 ///Required to indicate what is measurable. 10 struct NamedBenchmark 11 { 12 13 string name; 14 string module_; 15 int line; 16 this(string n, int l = __LINE__, string f = __MODULE__) 17 { 18 name = n; 19 line = l; 20 module_ = f; 21 } 22 23 JSONValue toJson() 24 { 25 return JSONValue([ 26 "name": JSONValue(name), 27 "module": JSONValue(module_), 28 "defined": JSONValue(line) 29 ]); 30 } 31 } 32 ///Enables timing of unittests, call runTests 33 template TimeTests(alias that) 34 { 35 version (unittest) 36 { 37 alias tests = __traits(getUnitTests, that); 38 39 /** 40 * Measure tests in a naive manner 41 42 * Params: 43 * iters = How many iterations to time each test 44 45 * Returns: JSONValue containing collected data 46 */ 47 JSONValue runTests(size_t iters) 48 { 49 struct TestResult 50 { 51 NamedBenchmark src; 52 float mean; 53 float min, max; 54 float stdandardDeviation; 55 this(NamedBenchmark s) 56 { 57 src = s; 58 } 59 60 JSONValue toJson() 61 { 62 return JSONValue([ 63 "src": src.toJson, 64 "timeUnit": JSONValue("us"), 65 "mean": JSONValue(mean), 66 "min": JSONValue(min), 67 "max": JSONValue(max), 68 "standardDeviation": JSONValue(stdandardDeviation) 69 ]); 70 } 71 } 72 73 TestResult[] tmp; 74 75 foreach (test; tests) 76 { 77 if (hasUDA!(test, NamedBenchmark)) 78 { 79 float[] usEachRun = new float[iters]; 80 81 const tag = getUDAs!(test, NamedBenchmark)[0]; 82 writef!("Measuring test %s from %s now\n")(tag.name, tag.module_); 83 auto res = TestResult(tag); 84 85 //Get data 86 foreach (ref i; usEachRun) 87 { 88 import std.datetime.stopwatch; 89 //Time the fucker 90 auto sw = StopWatch(AutoStart.no); 91 sw.start(); 92 93 test(); 94 95 sw.stop(); 96 97 i = cast(float) sw.peek.total!"usecs"; 98 99 } 100 101 //Process data 102 res.mean = sum(usEachRun) / iters; 103 res.min = minElement(usEachRun); 104 res.max = maxElement(usEachRun); 105 //Mean of the squares minus the square of the mean is wot i was taught init 106 import dstats.summary : stdev; 107 res.stdandardDeviation = usEachRun.stdev; 108 //writeln(JSONValue(usEachRun).toPrettyString); 109 tmp ~= res; 110 } 111 } 112 return JSONValue(tmp.map!(x => x.toJson).array); 113 } 114 } 115 } 116 117 118 /** Hijacks your main function and runs the tests in mod. 119 * mixin this to inject a C main that initializes the runtime and prints the tests etc. 120 * 121 * Params: 122 * mod = Where to gets the tests from 123 */ 124 template HijackMain(alias mod) 125 { 126 pragma(mangle, "main") 127 extern(C) 128 int main() 129 { 130 import core.runtime; 131 import std.stdio; 132 rt_init; 133 alias runThis = TimeTests!mod; 134 write("Iterations: \n"); 135 size_t iters; 136 readf!"%d\n"(iters); 137 138 puts("\n"); 139 140 141 writeln(runThis.runTests(iters).toPrettyString()); 142 143 144 rt_term(); 145 return 0; 146 } 147 } 148 ///just to get some compilation errors 149 @NamedBenchmark("test1") unittest 150 { 151 foreach(_; 0..10_000) 152 { 153 154 } 155 } 156 157 /// 158 @NamedBenchmark("main") unittest 159 { 160 //alias g = TimeTests!testquark; 161 } 162 163 //version(unittest) 164 //mixin HijackMain!testquark;