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