1 /**
2 Various simple benchmarks for the `checkedint` package.
3 
4 Copyright: Copyright Thomas Stuart Bockman 2015
5 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 Authors: Thomas Stuart Bockman
7 **/
8 module checkedint.tests.benchmark;
9 import checkedint, checkedint.flags, checkedint.traits;
10 alias IFP = IntFlagPolicy;
11 
12 import std.algorithm, std.stdio;
13 static if (__VERSION__ >= 2068)
14 {
15     version(GNU) { static assert(false); }
16     import std.meta : AliasSeq;
17 }
18 else
19     import std.typetuple : AliasSeq = TypeTuple;
20 
21 /+@safe:+/
22 
23 void benchMacro()
24 {
25     benchMacro!"trialPrimes"();
26     benchMacro!"collatzSort"();
27 }
28 
29 ulong checkSum;
30 bool invalid;
31 void benchMacro(string testStr)()
32 {
33     import std.conv : to;
34     import std.datetime : benchmark, Duration;
35 
36     mixin("alias test = " ~ testStr ~ ";");
37     writeln();
38     write("Running "); write(testStr); writeln("() benchmark... ");
39     function() @trusted { stdout.flush(); }();
40 
41     checkSum = 0;
42     test!int();
43     enum trials = 3;
44     enum laps = 3;
45     const compSum = checkSum * laps * trials;
46 
47     foreach (Vstr; AliasSeq!("int", "uint", "long", "ulong"))
48     {
49         foreach (Nstr; AliasSeq!(
50             Vstr,
51             "SafeInt!(" ~ Vstr ~ ", IFP.noex)",
52             "SafeInt!(" ~ Vstr ~ ", IFP.asserts)",
53             "SafeInt!(" ~ Vstr ~ ", IFP.throws)",
54             "SmartInt!(" ~ Vstr ~ ", IFP.noex)",
55             "SmartInt!(" ~ Vstr ~ ", IFP.asserts)",
56             "SmartInt!(" ~ Vstr ~ ", IFP.throws)"))
57         {
58             mixin("alias N = " ~ Nstr ~ ";");
59 
60             checkSum = 0;
61             invalid = false;
62             writef("%40s: ", Nstr);
63 
64             auto best = Duration.max;
65             foreach (i; 0 .. trials)
66             {
67                 const r = to!Duration(benchmark!(test!N)(laps)[0]) / laps;
68                 if (r < best)
69                     best = r;
70             }
71 
72             if (checkSum != compSum)
73                 write("!CHECKSUM ");
74             if (invalid)
75                 write("!INVALID ");
76             writeln(to!Duration(best));
77         }
78     }
79 
80     writeln("DONE");
81 }
82 
83 template SafeFold(N)
84 {
85     template SafeFold(real folded)
86     {
87         alias BN = BasicScalar!N;
88         static assert(BN.min <= folded && folded <= BN.max);
89         enum SafeFold = cast(BN)folded;
90     }
91 }
92 
93 // Unsafe high-speed shims:
94 private /+pragma(inline, true)+/
95 {
96     auto mulPow2(N, M)(const N left, const M exp)
97     {
98         return left << exp;
99     }
100     auto divPow2(N, M)(const N left, const M exp)
101     {
102         return left >> exp;
103     }
104     auto modPow2(N, M)(const N left, const M exp)
105     {
106         return left & ~(~cast(N)0 << exp);
107     }
108 
109     auto idx(N)(const N num)
110     {
111         static if (isCheckedInt!N)
112             return num.idx;
113         else static if (isSigned!N)
114             return cast(ptrdiff_t)num;
115         else
116             return cast(size_t)num;
117     }
118 }
119 
120 void trialPrimes(N)()
121 {
122     alias V = SafeFold!N;
123 
124     static auto floorSqrt(N n)
125     {
126         assert(n >= V!0);
127 
128         enum N maxT = isSigned!N?
129             V!((1414L.mulPow2((N.sizeof * 4) - 11)) - 1L) :
130             V!((1L.mulPow2(N.sizeof * 4)) - 1L);
131         N t = min((n/V!2 + V!1), maxT);
132         N r0 = t;
133         N sq0 = r0 * r0;
134         assert(n <= sq0);
135 
136         while (t >= V!1)
137         {
138             if (sq0 < n)
139             {
140                 const r1 = r0 + t;
141                 const sq1 = r1 * r1;
142 
143                 if (sq1 <= n)
144                 {
145                     r0 = r1;
146                     sq0 = sq1;
147                 }
148                 else
149                     t /= V!2;
150             }
151             else if (sq0 > n)
152             {
153                 r0 = r0 - t;
154                 sq0 = r0 * r0;
155 
156                 t /= V!2;
157             }
158             else
159                 break;
160         }
161 
162         return r0;
163     }
164 
165     enum PrimeCount = V!40_000;
166     N[PrimeCount] primes;
167     N[PrimeCount] primeRoots;
168 
169     N p = V!0;
170     primes[idx(p)] = N.max;
171     N n = V!2;
172     while (true)
173     {
174         const N rootN = floorSqrt(n);
175 
176         bool pass = true;
177         for (N dP = V!0; primes[idx(dP)] <= rootN; ++dP)
178         {
179             if (n % primes[idx(dP)] == V!0)
180             {
181                 pass = false;
182                 break;
183             }
184         }
185 
186         if (pass)
187         {
188             primes[idx(p)] = n;
189             primeRoots[idx(p)] = rootN;
190 
191             ++p;
192             if (p < PrimeCount)
193                 primes[idx(p)] = N.max;
194             else
195                 break;
196         }
197 
198         ++n;
199     }
200 
201     N subSum = V!0;
202     for (N pu = V!0; pu < PrimeCount; ++pu)
203         subSum += primeRoots[idx(pu)];
204     checkSum += subSum.bscal;
205     invalid = invalid || IntFlags.local;
206     IntFlags.local.clear();
207 
208   /+for (N q = 0; q < p; ++q) {
209         write(primes[idx(q)]);
210         write(" : ");
211         write(primeRoots[idx(q)]);
212         if (q % 8 != 7)
213             write(", ");
214         else
215             writeln();
216     }
217     writeln();
218     stdin.readln();+/
219 }
220 
221 void collatzSort(N)()
222 {
223     alias V = SafeFold!N;
224 
225     static void quickSort(N[] array, N lowX, N highX)
226     {
227         if (lowX >= highX)
228             return;
229 
230         N pivotX = (lowX + highX).divPow2(1u);
231         N pivotV = array[idx(pivotX)];
232         array[idx(pivotX)] = array[idx(highX)];
233         array[idx(highX)] = pivotV;
234 
235         N storeX = lowX;
236         N iX = lowX;
237         while (iX < highX)
238         {
239             const iV = array[idx(iX)];
240             if (iV < pivotV)
241             {
242                 array[idx(iX)] = array[idx(storeX)];
243                 array[idx(storeX)] = iV;
244                 ++storeX;
245             }
246             ++iX;
247         }
248         pivotX = storeX;
249 
250         pivotV = array[idx(pivotX)];
251         array[idx(pivotX)] = array[idx(highX)];
252         array[idx(highX)] = pivotV;
253 
254         quickSort(array, lowX, pivotX - V!1);
255         quickSort(array, pivotX + V!1, highX);
256     }
257 
258     enum N maxN = V!113_382;
259     N[idx(maxN)] finalIs;
260 
261     for (N n = V!0; n < maxN; ++n)
262     {
263         N i = V!0;
264         N ai = n + V!1;
265         while (ai != V!1)
266         {
267             if (ai.modPow2(1u) ==  V!0)
268                 ai = ai.divPow2(1u);
269             else
270                 ai = V!3*ai + V!1;
271 
272             ++i;
273         }
274 
275         finalIs[idx(n)] = i;
276     }
277 
278     quickSort(finalIs, N(V!0), maxN - V!1);
279 
280     N subSum = finalIs[0];
281     for (N x = V!1; x < maxN; ++x)
282     {
283         subSum += finalIs[idx(x)];
284         if (finalIs[idx(x)] < finalIs[idx(x - V!1)])
285             invalid = true;
286     }
287     checkSum += subSum.bscal;
288     invalid = invalid || IntFlags.local;
289     IntFlags.local.clear();
290 }