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