C++11 override should now be supported by all of {bots,Chrome,Android,Mozilla}
[platform/upstream/libSkiaSharp.git] / bench / MathBench.cpp
1 #include "Benchmark.h"
2 #include "SkColorPriv.h"
3 #include "SkMatrix.h"
4 #include "SkPaint.h"
5 #include "SkRandom.h"
6 #include "SkString.h"
7
8 static float sk_fsel(float pred, float result_ge, float result_lt) {
9     return pred >= 0 ? result_ge : result_lt;
10 }
11
12 static float fast_floor(float x) {
13 //    float big = sk_fsel(x, 0x1.0p+23, -0x1.0p+23);
14     float big = sk_fsel(x, (float)(1 << 23), -(float)(1 << 23));
15     return (x + big) - big;
16 }
17
18 class MathBench : public Benchmark {
19     enum {
20         kBuffer = 100,
21     };
22     SkString    fName;
23     float       fSrc[kBuffer], fDst[kBuffer];
24 public:
25     MathBench(const char name[])  {
26         fName.printf("math_%s", name);
27
28         SkRandom rand;
29         for (int i = 0; i < kBuffer; ++i) {
30             fSrc[i] = rand.nextSScalar1();
31         }
32     }
33
34     bool isSuitableFor(Backend backend) override {
35         return backend == kNonRendering_Backend;
36     }
37
38     virtual void performTest(float* SK_RESTRICT dst,
39                               const float* SK_RESTRICT src,
40                               int count) = 0;
41
42 protected:
43     virtual int mulLoopCount() const { return 1; }
44
45     virtual const char* onGetName() {
46         return fName.c_str();
47     }
48
49     virtual void onDraw(const int loops, SkCanvas*) {
50         int n = loops * this->mulLoopCount();
51         for (int i = 0; i < n; i++) {
52             this->performTest(fDst, fSrc, kBuffer);
53         }
54     }
55
56 private:
57     typedef Benchmark INHERITED;
58 };
59
60 class MathBenchU32 : public MathBench {
61 public:
62     MathBenchU32(const char name[]) : INHERITED(name) {}
63
64 protected:
65     virtual void performITest(uint32_t* SK_RESTRICT dst,
66                               const uint32_t* SK_RESTRICT src,
67                               int count) = 0;
68
69     virtual void performTest(float* SK_RESTRICT dst,
70                              const float* SK_RESTRICT src,
71                              int count) override {
72         uint32_t* d = SkTCast<uint32_t*>(dst);
73         const uint32_t* s = SkTCast<const uint32_t*>(src);
74         this->performITest(d, s, count);
75     }
76 private:
77     typedef MathBench INHERITED;
78 };
79
80 ///////////////////////////////////////////////////////////////////////////////
81
82 class NoOpMathBench : public MathBench {
83 public:
84     NoOpMathBench() : INHERITED("noOp") {}
85 protected:
86     virtual void performTest(float* SK_RESTRICT dst,
87                               const float* SK_RESTRICT src,
88                               int count) {
89         for (int i = 0; i < count; ++i) {
90             dst[i] = src[i] + 1;
91         }
92     }
93 private:
94     typedef MathBench INHERITED;
95 };
96
97 class SkRSqrtMathBench : public MathBench {
98 public:
99     SkRSqrtMathBench() : INHERITED("sk_float_rsqrt") {}
100 protected:
101     virtual void performTest(float* SK_RESTRICT dst,
102                               const float* SK_RESTRICT src,
103                               int count) {
104         for (int i = 0; i < count; ++i) {
105             dst[i] = sk_float_rsqrt(src[i]);
106         }
107     }
108 private:
109     typedef MathBench INHERITED;
110 };
111
112
113 class SlowISqrtMathBench : public MathBench {
114 public:
115     SlowISqrtMathBench() : INHERITED("slowIsqrt") {}
116 protected:
117     virtual void performTest(float* SK_RESTRICT dst,
118                               const float* SK_RESTRICT src,
119                               int count) {
120         for (int i = 0; i < count; ++i) {
121             dst[i] = 1.0f / sk_float_sqrt(src[i]);
122         }
123     }
124 private:
125     typedef MathBench INHERITED;
126 };
127
128 static inline float SkFastInvSqrt(float x) {
129     float xhalf = 0.5f*x;
130     int i = *SkTCast<int*>(&x);
131     i = 0x5f3759df - (i>>1);
132     x = *SkTCast<float*>(&i);
133     x = x*(1.5f-xhalf*x*x);
134 //    x = x*(1.5f-xhalf*x*x); // this line takes err from 10^-3 to 10^-6
135     return x;
136 }
137
138 class FastISqrtMathBench : public MathBench {
139 public:
140     FastISqrtMathBench() : INHERITED("fastIsqrt") {}
141 protected:
142     virtual void performTest(float* SK_RESTRICT dst,
143                               const float* SK_RESTRICT src,
144                               int count) {
145         for (int i = 0; i < count; ++i) {
146             dst[i] = SkFastInvSqrt(src[i]);
147         }
148     }
149 private:
150     typedef MathBench INHERITED;
151 };
152
153 static inline uint32_t QMul64(uint32_t value, U8CPU alpha) {
154     SkASSERT((uint8_t)alpha == alpha);
155     const uint32_t mask = 0xFF00FF;
156
157     uint64_t tmp = value;
158     tmp = (tmp & mask) | ((tmp & ~mask) << 24);
159     tmp *= alpha;
160     return (uint32_t) (((tmp >> 8) & mask) | ((tmp >> 32) & ~mask));
161 }
162
163 class QMul64Bench : public MathBenchU32 {
164 public:
165     QMul64Bench() : INHERITED("qmul64") {}
166 protected:
167     virtual void performITest(uint32_t* SK_RESTRICT dst,
168                               const uint32_t* SK_RESTRICT src,
169                               int count) override {
170         for (int i = 0; i < count; ++i) {
171             dst[i] = QMul64(src[i], (uint8_t)i);
172         }
173     }
174 private:
175     typedef MathBenchU32 INHERITED;
176 };
177
178 class QMul32Bench : public MathBenchU32 {
179 public:
180     QMul32Bench() : INHERITED("qmul32") {}
181 protected:
182     virtual void performITest(uint32_t* SK_RESTRICT dst,
183                               const uint32_t* SK_RESTRICT src,
184                               int count) override {
185         for (int i = 0; i < count; ++i) {
186             dst[i] = SkAlphaMulQ(src[i], (uint8_t)i);
187         }
188     }
189 private:
190     typedef MathBenchU32 INHERITED;
191 };
192
193 ///////////////////////////////////////////////////////////////////////////////
194
195 static bool isFinite_int(float x) {
196     uint32_t bits = SkFloat2Bits(x);    // need unsigned for our shifts
197     int exponent = bits << 1 >> 24;
198     return exponent != 0xFF;
199 }
200
201 static bool isFinite_float(float x) {
202     return SkToBool(sk_float_isfinite(x));
203 }
204
205 static bool isFinite_mulzero(float x) {
206     float y = x * 0;
207     return y == y;
208 }
209
210 static bool isfinite_and_int(const float data[4]) {
211     return  isFinite_int(data[0]) && isFinite_int(data[1]) && isFinite_int(data[2]) && isFinite_int(data[3]);
212 }
213
214 static bool isfinite_and_float(const float data[4]) {
215     return  isFinite_float(data[0]) && isFinite_float(data[1]) && isFinite_float(data[2]) && isFinite_float(data[3]);
216 }
217
218 static bool isfinite_and_mulzero(const float data[4]) {
219     return  isFinite_mulzero(data[0]) && isFinite_mulzero(data[1]) && isFinite_mulzero(data[2]) && isFinite_mulzero(data[3]);
220 }
221
222 #define mulzeroadd(data)    (data[0]*0 + data[1]*0 + data[2]*0 + data[3]*0)
223
224 static bool isfinite_plus_int(const float data[4]) {
225     return  isFinite_int(mulzeroadd(data));
226 }
227
228 static bool isfinite_plus_float(const float data[4]) {
229     return  !sk_float_isnan(mulzeroadd(data));
230 }
231
232 static bool isfinite_plus_mulzero(const float data[4]) {
233     float x = mulzeroadd(data);
234     return x == x;
235 }
236
237 typedef bool (*IsFiniteProc)(const float[]);
238
239 #define MAKEREC(name)   { name, #name }
240
241 static const struct {
242     IsFiniteProc    fProc;
243     const char*     fName;
244 } gRec[] = {
245     MAKEREC(isfinite_and_int),
246     MAKEREC(isfinite_and_float),
247     MAKEREC(isfinite_and_mulzero),
248     MAKEREC(isfinite_plus_int),
249     MAKEREC(isfinite_plus_float),
250     MAKEREC(isfinite_plus_mulzero),
251 };
252
253 #undef MAKEREC
254
255 static bool isFinite(const SkRect& r) {
256     // x * 0 will be NaN iff x is infinity or NaN.
257     // a + b will be NaN iff either a or b is NaN.
258     float value = r.fLeft * 0 + r.fTop * 0 + r.fRight * 0 + r.fBottom * 0;
259
260     // value is either NaN or it is finite (zero).
261     // value==value will be true iff value is not NaN
262     return value == value;
263 }
264
265 class IsFiniteBench : public Benchmark {
266     enum {
267         N = 1000,
268     };
269     float fData[N];
270 public:
271
272     IsFiniteBench(int index)  {
273         SkRandom rand;
274
275         for (int i = 0; i < N; ++i) {
276             fData[i] = rand.nextSScalar1();
277         }
278
279         if (index < 0) {
280             fProc = NULL;
281             fName = "isfinite_rect";
282         } else {
283             fProc = gRec[index].fProc;
284             fName = gRec[index].fName;
285         }
286     }
287
288     bool isSuitableFor(Backend backend) override {
289         return backend == kNonRendering_Backend;
290     }
291
292 protected:
293     virtual void onDraw(const int loops, SkCanvas*) {
294         IsFiniteProc proc = fProc;
295         const float* data = fData;
296         // do this so the compiler won't throw away the function call
297         int counter = 0;
298
299         if (proc) {
300             for (int j = 0; j < loops; ++j) {
301                 for (int i = 0; i < N - 4; ++i) {
302                     counter += proc(&data[i]);
303                 }
304             }
305         } else {
306             for (int j = 0; j < loops; ++j) {
307                 for (int i = 0; i < N - 4; ++i) {
308                     const SkRect* r = reinterpret_cast<const SkRect*>(&data[i]);
309                     if (false) { // avoid bit rot, suppress warning
310                         isFinite(*r);
311                     }
312                     counter += r->isFinite();
313                 }
314             }
315         }
316
317         SkPaint paint;
318         if (paint.getAlpha() == 0) {
319             SkDebugf("%d\n", counter);
320         }
321     }
322
323     virtual const char* onGetName() {
324         return fName;
325     }
326
327 private:
328     IsFiniteProc    fProc;
329     const char*     fName;
330
331     typedef Benchmark INHERITED;
332 };
333
334 class FloorBench : public Benchmark {
335     enum {
336         ARRAY = 1000,
337     };
338     float fData[ARRAY];
339     bool fFast;
340 public:
341
342     FloorBench(bool fast) : fFast(fast) {
343         SkRandom rand;
344
345         for (int i = 0; i < ARRAY; ++i) {
346             fData[i] = rand.nextSScalar1();
347         }
348
349         if (fast) {
350             fName = "floor_fast";
351         } else {
352             fName = "floor_std";
353         }
354     }
355
356     bool isSuitableFor(Backend backend) override {
357         return backend == kNonRendering_Backend;
358     }
359
360     virtual void process(float) {}
361
362 protected:
363     virtual void onDraw(const int loops, SkCanvas*) {
364         SkRandom rand;
365         float accum = 0;
366         const float* data = fData;
367
368         if (fFast) {
369             for (int j = 0; j < loops; ++j) {
370                 for (int i = 0; i < ARRAY; ++i) {
371                     accum += fast_floor(data[i]);
372                 }
373                 this->process(accum);
374             }
375         } else {
376             for (int j = 0; j < loops; ++j) {
377                 for (int i = 0; i < ARRAY; ++i) {
378                     accum += sk_float_floor(data[i]);
379                 }
380                 this->process(accum);
381             }
382         }
383     }
384
385     virtual const char* onGetName() {
386         return fName;
387     }
388
389 private:
390     const char*     fName;
391
392     typedef Benchmark INHERITED;
393 };
394
395 class CLZBench : public Benchmark {
396     enum {
397         ARRAY = 1000,
398     };
399     uint32_t fData[ARRAY];
400     bool fUsePortable;
401
402 public:
403     CLZBench(bool usePortable) : fUsePortable(usePortable) {
404
405         SkRandom rand;
406         for (int i = 0; i < ARRAY; ++i) {
407             fData[i] = rand.nextU();
408         }
409
410         if (fUsePortable) {
411             fName = "clz_portable";
412         } else {
413             fName = "clz_intrinsic";
414         }
415     }
416
417     bool isSuitableFor(Backend backend) override {
418         return backend == kNonRendering_Backend;
419     }
420
421     // just so the compiler doesn't remove our loops
422     virtual void process(int) {}
423
424 protected:
425     virtual void onDraw(const int loops, SkCanvas*) {
426         int accum = 0;
427
428         if (fUsePortable) {
429             for (int j = 0; j < loops; ++j) {
430                 for (int i = 0; i < ARRAY; ++i) {
431                     accum += SkCLZ_portable(fData[i]);
432                 }
433                 this->process(accum);
434             }
435         } else {
436             for (int j = 0; j < loops; ++j) {
437                 for (int i = 0; i < ARRAY; ++i) {
438                     accum += SkCLZ(fData[i]);
439                 }
440                 this->process(accum);
441             }
442         }
443     }
444
445     virtual const char* onGetName() {
446         return fName;
447     }
448
449 private:
450     const char* fName;
451
452     typedef Benchmark INHERITED;
453 };
454
455 ///////////////////////////////////////////////////////////////////////////////
456
457 class NormalizeBench : public Benchmark {
458     enum {
459         ARRAY =1000,
460     };
461     SkVector fVec[ARRAY];
462
463 public:
464     NormalizeBench() {
465         SkRandom rand;
466         for (int i = 0; i < ARRAY; ++i) {
467             fVec[i].set(rand.nextSScalar1(), rand.nextSScalar1());
468         }
469
470         fName = "point_normalize";
471     }
472
473     bool isSuitableFor(Backend backend) override {
474         return backend == kNonRendering_Backend;
475     }
476
477     // just so the compiler doesn't remove our loops
478     virtual void process(int) {}
479
480 protected:
481     virtual void onDraw(const int loops, SkCanvas*) {
482         int accum = 0;
483
484         for (int j = 0; j < loops; ++j) {
485             for (int i = 0; i < ARRAY; ++i) {
486                 accum += fVec[i].normalize();
487             }
488             this->process(accum);
489         }
490     }
491
492     virtual const char* onGetName() {
493         return fName;
494     }
495
496 private:
497     const char* fName;
498
499     typedef Benchmark INHERITED;
500 };
501
502 ///////////////////////////////////////////////////////////////////////////////
503
504 class FixedMathBench : public Benchmark {
505     enum {
506         N = 1000,
507     };
508     float fData[N];
509     SkFixed fResult[N];
510 public:
511
512     FixedMathBench()  {
513         SkRandom rand;
514         for (int i = 0; i < N; ++i) {
515             fData[i] = rand.nextSScalar1();
516         }
517
518     }
519
520     bool isSuitableFor(Backend backend) override {
521         return backend == kNonRendering_Backend;
522     }
523
524 protected:
525     virtual void onDraw(const int loops, SkCanvas*) {
526         for (int j = 0; j < loops; ++j) {
527             for (int i = 0; i < N - 4; ++i) {
528                 fResult[i] = SkFloatToFixed(fData[i]);
529             }
530         }
531
532         SkPaint paint;
533         if (paint.getAlpha() == 0) {
534             SkDebugf("%d\n", fResult[0]);
535         }
536     }
537
538     virtual const char* onGetName() {
539         return "float_to_fixed";
540     }
541
542 private:
543     typedef Benchmark INHERITED;
544 };
545
546 ///////////////////////////////////////////////////////////////////////////////
547
548 template <typename T>
549 class DivModBench : public Benchmark {
550     SkString fName;
551 public:
552     explicit DivModBench(const char* name) {
553         fName.printf("divmod_%s", name);
554     }
555
556     bool isSuitableFor(Backend backend) override {
557         return backend == kNonRendering_Backend;
558     }
559
560 protected:
561     virtual const char* onGetName() {
562         return fName.c_str();
563     }
564
565     virtual void onDraw(const int loops, SkCanvas*) {
566         volatile T a = 0, b = 0;
567         T div = 0, mod = 0;
568         for (int i = 0; i < loops; i++) {
569             if ((T)i == 0) continue;  // Small T will wrap around.
570             SkTDivMod((T)(i+1), (T)i, &div, &mod);
571             a ^= div;
572             b ^= mod;
573         }
574     }
575 };
576 DEF_BENCH(return new DivModBench<uint8_t>("uint8_t"))
577 DEF_BENCH(return new DivModBench<uint16_t>("uint16_t"))
578 DEF_BENCH(return new DivModBench<uint32_t>("uint32_t"))
579 DEF_BENCH(return new DivModBench<uint64_t>("uint64_t"))
580
581 DEF_BENCH(return new DivModBench<int8_t>("int8_t"))
582 DEF_BENCH(return new DivModBench<int16_t>("int16_t"))
583 DEF_BENCH(return new DivModBench<int32_t>("int32_t"))
584 DEF_BENCH(return new DivModBench<int64_t>("int64_t"))
585
586 ///////////////////////////////////////////////////////////////////////////////
587
588 DEF_BENCH( return new NoOpMathBench(); )
589 DEF_BENCH( return new SkRSqrtMathBench(); )
590 DEF_BENCH( return new SlowISqrtMathBench(); )
591 DEF_BENCH( return new FastISqrtMathBench(); )
592 DEF_BENCH( return new QMul64Bench(); )
593 DEF_BENCH( return new QMul32Bench(); )
594
595 DEF_BENCH( return new IsFiniteBench(-1); )
596 DEF_BENCH( return new IsFiniteBench(0); )
597 DEF_BENCH( return new IsFiniteBench(1); )
598 DEF_BENCH( return new IsFiniteBench(2); )
599 DEF_BENCH( return new IsFiniteBench(3); )
600 DEF_BENCH( return new IsFiniteBench(4); )
601 DEF_BENCH( return new IsFiniteBench(5); )
602
603 DEF_BENCH( return new FloorBench(false); )
604 DEF_BENCH( return new FloorBench(true); )
605
606 DEF_BENCH( return new CLZBench(false); )
607 DEF_BENCH( return new CLZBench(true); )
608
609 DEF_BENCH( return new NormalizeBench(); )
610
611 DEF_BENCH( return new FixedMathBench(); )