• 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     using INHERITED = Benchmark;
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     using INHERITED = MathBench;
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     using INHERITED = MathBench;
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     using INHERITED = MathBench;
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     using INHERITED = MathBench;
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     using INHERITED = MathBench;
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     using INHERITED = MathBenchU32;
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     using INHERITED = MathBenchU32;
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     using INHERITED = Benchmark;
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     using INHERITED = Benchmark;
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     using INHERITED = Benchmark;
442 };
443 
444 class CTZBench : public Benchmark {
445     enum {
446         ARRAY = 1000,
447     };
448     uint32_t fData[ARRAY];
449     bool fUsePortable;
450 
451 public:
CTZBench(bool usePortable)452     CTZBench(bool usePortable) : fUsePortable(usePortable) {
453 
454         SkRandom rand;
455         for (int i = 0; i < ARRAY; ++i) {
456             fData[i] = rand.nextU();
457         }
458 
459         if (fUsePortable) {
460             fName = "ctz_portable";
461         } else {
462             fName = "ctz_intrinsic";
463         }
464     }
465 
isSuitableFor(Backend backend)466     bool isSuitableFor(Backend backend) override {
467         return backend == kNonRendering_Backend;
468     }
469 
470     // just so the compiler doesn't remove our loops
process(int)471     virtual void process(int) {}
472 
473 protected:
onDraw(int loops,SkCanvas *)474     void onDraw(int loops, SkCanvas*) override {
475         int accum = 0;
476 
477         if (fUsePortable) {
478             for (int j = 0; j < loops; ++j) {
479                 for (int i = 0; i < ARRAY; ++i) {
480                     accum += SkCTZ_portable(fData[i]);
481                 }
482                 this->process(accum);
483             }
484         } else {
485             for (int j = 0; j < loops; ++j) {
486                 for (int i = 0; i < ARRAY; ++i) {
487                     accum += SkCTZ(fData[i]);
488                 }
489                 this->process(accum);
490             }
491         }
492     }
493 
onGetName()494     const char* onGetName() override {
495         return fName;
496     }
497 
498 private:
499     const char* fName;
500 
501     using INHERITED = Benchmark;
502 };
503 
504 ///////////////////////////////////////////////////////////////////////////////
505 
506 class NormalizeBench : public Benchmark {
507     enum {
508         ARRAY =1000,
509     };
510     SkVector fVec[ARRAY];
511 
512 public:
NormalizeBench()513     NormalizeBench() {
514         SkRandom rand;
515         for (int i = 0; i < ARRAY; ++i) {
516             fVec[i].set(rand.nextSScalar1(), rand.nextSScalar1());
517         }
518 
519         fName = "point_normalize";
520     }
521 
isSuitableFor(Backend backend)522     bool isSuitableFor(Backend backend) override {
523         return backend == kNonRendering_Backend;
524     }
525 
526     // just so the compiler doesn't remove our loops
process(int)527     virtual void process(int) {}
528 
529 protected:
onDraw(int loops,SkCanvas *)530     void onDraw(int loops, SkCanvas*) override {
531         int accum = 0;
532 
533         for (int j = 0; j < loops; ++j) {
534             for (int i = 0; i < ARRAY; ++i) {
535                 accum += fVec[i].normalize();
536             }
537             this->process(accum);
538         }
539     }
540 
onGetName()541     const char* onGetName() override {
542         return fName;
543     }
544 
545 private:
546     const char* fName;
547 
548     using INHERITED = Benchmark;
549 };
550 
551 ///////////////////////////////////////////////////////////////////////////////
552 
553 class FixedMathBench : public Benchmark {
554     enum {
555         N = 1000,
556     };
557     float fData[N];
558     SkFixed fResult[N];
559 public:
560 
FixedMathBench()561     FixedMathBench()  {
562         SkRandom rand;
563         for (int i = 0; i < N; ++i) {
564             fData[i] = rand.nextSScalar1();
565         }
566 
567     }
568 
isSuitableFor(Backend backend)569     bool isSuitableFor(Backend backend) override {
570         return backend == kNonRendering_Backend;
571     }
572 
573 protected:
onDraw(int loops,SkCanvas *)574     void onDraw(int loops, SkCanvas*) override {
575         for (int j = 0; j < loops; ++j) {
576             for (int i = 0; i < N - 4; ++i) {
577                 fResult[i] = SkFloatToFixed(fData[i]);
578             }
579         }
580 
581         SkPaint paint;
582         if (paint.getAlpha() == 0) {
583             SkDebugf("%d\n", fResult[0]);
584         }
585     }
586 
onGetName()587     const char* onGetName() override {
588         return "float_to_fixed";
589     }
590 
591 private:
592     using INHERITED = Benchmark;
593 };
594 
595 ///////////////////////////////////////////////////////////////////////////////
596 
597 template <typename T>
598 class DivModBench : public Benchmark {
599     SkString fName;
600 public:
DivModBench(const char * name)601     explicit DivModBench(const char* name) {
602         fName.printf("divmod_%s", name);
603     }
604 
isSuitableFor(Backend backend)605     bool isSuitableFor(Backend backend) override {
606         return backend == kNonRendering_Backend;
607     }
608 
609 protected:
onGetName()610     const char* onGetName() override {
611         return fName.c_str();
612     }
613 
onDraw(int loops,SkCanvas *)614     void onDraw(int loops, SkCanvas*) override {
615         volatile T a = 0, b = 0;
616         T div = 0, mod = 0;
617         for (int i = 0; i < loops; i++) {
618             if ((T)i == 0) continue;  // Small T will wrap around.
619             SkTDivMod((T)(i+1), (T)i, &div, &mod);
620             a ^= div;
621             b ^= mod;
622         }
623     }
624 };
625 DEF_BENCH(return new DivModBench<uint8_t>("uint8_t"))
626 DEF_BENCH(return new DivModBench<uint16_t>("uint16_t"))
627 DEF_BENCH(return new DivModBench<uint32_t>("uint32_t"))
628 DEF_BENCH(return new DivModBench<uint64_t>("uint64_t"))
629 
630 DEF_BENCH(return new DivModBench<int8_t>("int8_t"))
631 DEF_BENCH(return new DivModBench<int16_t>("int16_t"))
632 DEF_BENCH(return new DivModBench<int32_t>("int32_t"))
633 DEF_BENCH(return new DivModBench<int64_t>("int64_t"))
634 
635 ///////////////////////////////////////////////////////////////////////////////
636 
637 DEF_BENCH( return new NoOpMathBench(); )
638 DEF_BENCH( return new SkRSqrtMathBench(); )
639 DEF_BENCH( return new SlowISqrtMathBench(); )
640 DEF_BENCH( return new FastISqrtMathBench(); )
641 DEF_BENCH( return new QMul64Bench(); )
642 DEF_BENCH( return new QMul32Bench(); )
643 
644 DEF_BENCH( return new IsFiniteBench(-1); )
645 DEF_BENCH( return new IsFiniteBench(0); )
646 DEF_BENCH( return new IsFiniteBench(1); )
647 DEF_BENCH( return new IsFiniteBench(2); )
648 DEF_BENCH( return new IsFiniteBench(3); )
649 DEF_BENCH( return new IsFiniteBench(4); )
650 DEF_BENCH( return new IsFiniteBench(5); )
651 
652 DEF_BENCH( return new FloorBench(false); )
653 DEF_BENCH( return new FloorBench(true); )
654 
655 DEF_BENCH( return new CLZBench(false); )
656 DEF_BENCH( return new CLZBench(true); )
657 DEF_BENCH( return new CTZBench(false); )
658 DEF_BENCH( return new CTZBench(true); )
659 
660 DEF_BENCH( return new NormalizeBench(); )
661 
662 DEF_BENCH( return new FixedMathBench(); )
663 
664 //////////////////////////////////////////////////////////////
665 
666 #include "include/private/SkFloatBits.h"
667 class Floor2IntBench : public Benchmark {
668     enum {
669         ARRAY = 1000,
670     };
671     float fData[ARRAY];
672     const bool fSat;
673 public:
674 
Floor2IntBench(bool sat)675     Floor2IntBench(bool sat) : fSat(sat) {
676         SkRandom rand;
677 
678         for (int i = 0; i < ARRAY; ++i) {
679             fData[i] = SkBits2Float(rand.nextU());
680         }
681 
682         if (sat) {
683             fName = "floor2int_sat";
684         } else {
685             fName = "floor2int_undef";
686         }
687     }
688 
isSuitableFor(Backend backend)689     bool isSuitableFor(Backend backend) override {
690         return backend == kNonRendering_Backend;
691     }
692 
693     // These exist to try to stop the compiler from detecting what we doing, and throwing
694     // parts away (or knowing exactly how big the loop counts are).
process(unsigned)695     virtual void process(unsigned) {}
count()696     virtual int count() { return ARRAY; }
697 
698 protected:
onDraw(int loops,SkCanvas *)699     void onDraw(int loops, SkCanvas*) override {
700         // used unsigned to avoid undefined behavior if/when the += might overflow
701         unsigned accum = 0;
702 
703         for (int j = 0; j < loops; ++j) {
704             int n = this->count();
705             if (fSat) {
706                 for (int i = 0; i < n; ++i) {
707                     accum += sk_float_floor2int(fData[i]);
708                 }
709             } else {
710                 for (int i = 0; i < n; ++i) {
711                     accum += sk_float_floor2int_no_saturate(fData[i]);
712                 }
713             }
714             this->process(accum);
715         }
716     }
717 
onGetName()718     const char* onGetName() override { return fName; }
719 
720 private:
721     const char* fName;
722 
723     using INHERITED = Benchmark;
724 };
725 DEF_BENCH( return new Floor2IntBench(false); )
726 DEF_BENCH( return new Floor2IntBench(true); )
727 
728