1 /**
2  * Simple speed shootout benchmark between the generators
3  * implemented in this package and the default phobos
4  * random number generator.
5  *
6  * Usage:
7  * ------
8  * dxorshift_benchmark [-n N] [--seed S]
9  *
10  *           -n  specify number of calls to the RNG
11  *               in the benchmark; if not specified,
12  *               10 ^^ 9 will be used by default
13  *
14  *   --seed, -s  specify the seed to provide to the
15  *               benchmarked RNGS; if not specified,
16  *               an unpredictable seed will be used.
17  * ------
18  *
19  * Authors:
20  *     $(LINK2 http://braingam.es/, Joseph Rushton Wakeling)
21  *
22  * Copyright:
23  *     Written in 2016 by Joseph Rushton Wakeling.
24  *
25  * License:
26  *     $(LINK2 https://creativecommons.org/publicdomain/zero/1.0/legalcode, Creative Commons CC0)
27  *     (public domain)
28  */
29 module benchmark.app;
30 
31 void main(string[] args)
32 {
33     import std.conv : to;
34     import std.datetime : benchmark, Duration;
35     import std.getopt : getopt;
36     import std.random : Random, unpredictableSeed;
37     import std.stdio : writeln, writefln;
38 
39     import dxorshift.splitmix64;
40     import dxorshift.xoroshiro128plus;
41     import dxorshift.xorshift1024star;
42 
43     uint repeats;
44 
45     uint seed;
46 
47     getopt(args,
48            "|n", &repeats,
49            "seed|s", &seed);
50 
51     if (!repeats)
52     {
53         repeats = 1_000_000_000u;
54         writefln("Number of variates not specified: defaulting to %s", repeats);
55     }
56 
57     if (!seed)
58     {
59         seed = unpredictableSeed;
60     }
61 
62     writefln("Seeding generators with %s", seed);
63 
64     auto genX128 = Xoroshiro128plus(seed);
65 
66     auto genX1024 = Xorshift1024star(seed);
67 
68     auto genSM64 = SplitMix64(seed);
69 
70     auto genDefault = Random(seed);
71 
72     // summing over all generated variates adds
73     // a little overhead, but prevents spurious
74     // optimizations of generator `popFront()`
75     // calls: e.g. since `SplitMix64.popFront()`
76     // is just an addition, any arbitrary number
77     // of calls can be optimized away at compile
78     // time unless `.front` values are actually
79     // used for something
80     ulong variateSum;
81 
82     void variateX128()
83     {
84         variateSum += genX128.front;
85         genX128.popFront();
86     }
87 
88     void variateX1024()
89     {
90         variateSum += genX1024.front;
91         genX1024.popFront();
92     }
93 
94     void variateSM64()
95     {
96         variateSum += genSM64.front;
97         genSM64.popFront();
98     }
99 
100     void variateDefault()
101     {
102         variateSum += genDefault.front;
103         genDefault.popFront();
104     }
105 
106 
107     auto bench = benchmark!(variateX128, variateX1024, variateSM64, variateDefault)(repeats);
108     auto benchX128 = to!Duration(bench[0]);
109     auto benchX1024 = to!Duration(bench[1]);
110     auto benchSM64 = to!Duration(bench[2]);
111     auto benchDefault = to!Duration(bench[3]);
112 
113     writefln("xoroshiro128+ benchmark for %s variates: %s", repeats, benchX128);
114     writefln("last variate: %s", genX128.front);
115     writeln();
116 
117     writefln("xorshift1024* benchmark for %s variates: %s", repeats, benchX1024);
118     writefln("last variate: %s", genX1024.front);
119     writeln();
120 
121     writefln("splitmix64 benchmark for %s variates: %s", repeats, benchSM64);
122     writefln("last variate: %s", genSM64.front);
123     writeln();
124 
125     writefln("Default phobos RNG benchmark for %s variates: %s", repeats, benchDefault);
126     writefln("last variate: %s", genDefault.front);
127 }