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