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 "SkColorData.h"
10 #include "SkFixed.h"
11 #include "SkMathPriv.h"
12 #include "SkMatrix.h"
13 #include "SkPaint.h"
14 #include "SkRandom.h"
15 #include "SkString.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 "../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