• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "SkBenchmark.h"
2 #include "SkColorPriv.h"
3 #include "SkMatrix.h"
4 #include "SkRandom.h"
5 #include "SkString.h"
6 #include "SkPaint.h"
7 
sk_fsel(float pred,float result_ge,float result_lt)8 static float sk_fsel(float pred, float result_ge, float result_lt) {
9     return pred >= 0 ? result_ge : result_lt;
10 }
11 
fast_floor(float x)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 SkBenchmark {
19     enum {
20         kBuffer = 100,
21     };
22     SkString    fName;
23     float       fSrc[kBuffer], fDst[kBuffer];
24 public:
MathBench(const char name[])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 
isSuitableFor(Backend backend)34     virtual bool isSuitableFor(Backend backend) SK_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:
mulLoopCount() const43     virtual int mulLoopCount() const { return 1; }
44 
onGetName()45     virtual const char* onGetName() {
46         return fName.c_str();
47     }
48 
onDraw(const int loops,SkCanvas *)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 SkBenchmark INHERITED;
58 };
59 
60 class MathBenchU32 : public MathBench {
61 public:
MathBenchU32(const char name[])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 
performTest(float * SK_RESTRICT dst,const float * SK_RESTRICT src,int count)69     virtual void performTest(float* SK_RESTRICT dst,
70                              const float* SK_RESTRICT src,
71                              int count) SK_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:
NoOpMathBench()84     NoOpMathBench() : INHERITED("noOp") {}
85 protected:
performTest(float * SK_RESTRICT dst,const float * SK_RESTRICT src,int count)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:
SkRSqrtMathBench()99     SkRSqrtMathBench() : INHERITED("sk_float_rsqrt") {}
100 protected:
performTest(float * SK_RESTRICT dst,const float * SK_RESTRICT src,int count)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:
SlowISqrtMathBench()115     SlowISqrtMathBench() : INHERITED("slowIsqrt") {}
116 protected:
performTest(float * SK_RESTRICT dst,const float * SK_RESTRICT src,int count)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 
SkFastInvSqrt(float x)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:
FastISqrtMathBench()140     FastISqrtMathBench() : INHERITED("fastIsqrt") {}
141 protected:
performTest(float * SK_RESTRICT dst,const float * SK_RESTRICT src,int count)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 
QMul64(uint32_t value,U8CPU alpha)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:
QMul64Bench()165     QMul64Bench() : INHERITED("qmul64") {}
166 protected:
performITest(uint32_t * SK_RESTRICT dst,const uint32_t * SK_RESTRICT src,int count)167     virtual void performITest(uint32_t* SK_RESTRICT dst,
168                               const uint32_t* SK_RESTRICT src,
169                               int count) SK_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:
QMul32Bench()180     QMul32Bench() : INHERITED("qmul32") {}
181 protected:
performITest(uint32_t * SK_RESTRICT dst,const uint32_t * SK_RESTRICT src,int count)182     virtual void performITest(uint32_t* SK_RESTRICT dst,
183                               const uint32_t* SK_RESTRICT src,
184                               int count) SK_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 
isFinite_int(float x)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 
isFinite_float(float x)201 static bool isFinite_float(float x) {
202     return SkToBool(sk_float_isfinite(x));
203 }
204 
isFinite_mulzero(float x)205 static bool isFinite_mulzero(float x) {
206     float y = x * 0;
207     return y == y;
208 }
209 
isfinite_and_int(const float data[4])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 
isfinite_and_float(const float data[4])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 
isfinite_and_mulzero(const float data[4])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 
isfinite_plus_int(const float data[4])224 static bool isfinite_plus_int(const float data[4]) {
225     return  isFinite_int(mulzeroadd(data));
226 }
227 
isfinite_plus_float(const float data[4])228 static bool isfinite_plus_float(const float data[4]) {
229     return  !sk_float_isnan(mulzeroadd(data));
230 }
231 
isfinite_plus_mulzero(const float data[4])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 
isFinite(const SkRect & r)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 SkBenchmark {
266     enum {
267         N = 1000,
268     };
269     float fData[N];
270 public:
271 
IsFiniteBench(int index)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 
isSuitableFor(Backend backend)288     virtual bool isSuitableFor(Backend backend) SK_OVERRIDE {
289         return backend == kNonRendering_Backend;
290     }
291 
292 protected:
onDraw(const int loops,SkCanvas *)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 
onGetName()323     virtual const char* onGetName() {
324         return fName;
325     }
326 
327 private:
328     IsFiniteProc    fProc;
329     const char*     fName;
330 
331     typedef SkBenchmark INHERITED;
332 };
333 
334 class FloorBench : public SkBenchmark {
335     enum {
336         ARRAY = 1000,
337     };
338     float fData[ARRAY];
339     bool fFast;
340 public:
341 
FloorBench(bool fast)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 
isSuitableFor(Backend backend)356     virtual bool isSuitableFor(Backend backend) SK_OVERRIDE {
357         return backend == kNonRendering_Backend;
358     }
359 
process(float)360     virtual void process(float) {}
361 
362 protected:
onDraw(const int loops,SkCanvas *)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 
onGetName()385     virtual const char* onGetName() {
386         return fName;
387     }
388 
389 private:
390     const char*     fName;
391 
392     typedef SkBenchmark INHERITED;
393 };
394 
395 class CLZBench : public SkBenchmark {
396     enum {
397         ARRAY = 1000,
398     };
399     uint32_t fData[ARRAY];
400     bool fUsePortable;
401 
402 public:
CLZBench(bool usePortable)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 
isSuitableFor(Backend backend)417     virtual bool isSuitableFor(Backend backend) SK_OVERRIDE {
418         return backend == kNonRendering_Backend;
419     }
420 
421     // just so the compiler doesn't remove our loops
process(int)422     virtual void process(int) {}
423 
424 protected:
onDraw(const int loops,SkCanvas *)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 
onGetName()445     virtual const char* onGetName() {
446         return fName;
447     }
448 
449 private:
450     const char* fName;
451 
452     typedef SkBenchmark INHERITED;
453 };
454 
455 ///////////////////////////////////////////////////////////////////////////////
456 
457 class NormalizeBench : public SkBenchmark {
458     enum {
459         ARRAY =1000,
460     };
461     SkVector fVec[ARRAY];
462 
463 public:
NormalizeBench()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 
isSuitableFor(Backend backend)473     virtual bool isSuitableFor(Backend backend) SK_OVERRIDE {
474         return backend == kNonRendering_Backend;
475     }
476 
477     // just so the compiler doesn't remove our loops
process(int)478     virtual void process(int) {}
479 
480 protected:
onDraw(const int loops,SkCanvas *)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 
onGetName()492     virtual const char* onGetName() {
493         return fName;
494     }
495 
496 private:
497     const char* fName;
498 
499     typedef SkBenchmark INHERITED;
500 };
501 
502 ///////////////////////////////////////////////////////////////////////////////
503 
504 class FixedMathBench : public SkBenchmark {
505     enum {
506         N = 1000,
507     };
508     float fData[N];
509     SkFixed fResult[N];
510 public:
511 
FixedMathBench()512     FixedMathBench()  {
513         SkRandom rand;
514         for (int i = 0; i < N; ++i) {
515             fData[i] = rand.nextSScalar1();
516         }
517 
518     }
519 
isSuitableFor(Backend backend)520     virtual bool isSuitableFor(Backend backend) SK_OVERRIDE {
521         return backend == kNonRendering_Backend;
522     }
523 
524 protected:
onDraw(const int loops,SkCanvas *)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 
onGetName()538     virtual const char* onGetName() {
539         return "float_to_fixed";
540     }
541 
542 private:
543     typedef SkBenchmark INHERITED;
544 };
545 
546 ///////////////////////////////////////////////////////////////////////////////
547 
548 template <typename T>
549 class DivModBench : public SkBenchmark {
550     SkString fName;
551 public:
DivModBench(const char * name)552     explicit DivModBench(const char* name) {
553         fName.printf("divmod_%s", name);
554     }
555 
isSuitableFor(Backend backend)556     virtual bool isSuitableFor(Backend backend) SK_OVERRIDE {
557         return backend == kNonRendering_Backend;
558     }
559 
560 protected:
onGetName()561     virtual const char* onGetName() {
562         return fName.c_str();
563     }
564 
onDraw(const int loops,SkCanvas *)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(); )
612