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