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/base/SkFixed.h"
14 #include "src/base/SkMathPriv.h"
15 #include "src/base/SkRandom.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 == Backend::kNonRendering;
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 / std::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_mulzero(float x)190 static bool isFinite_mulzero(float x) {
191 float y = x * 0;
192 return y == y;
193 }
194
isfinite_and_int(const float data[4])195 static bool isfinite_and_int(const float data[4]) {
196 return isFinite_int(data[0]) && isFinite_int(data[1]) && isFinite_int(data[2]) && isFinite_int(data[3]);
197 }
198
isfinite_and_mulzero(const float data[4])199 static bool isfinite_and_mulzero(const float data[4]) {
200 return isFinite_mulzero(data[0]) && isFinite_mulzero(data[1]) && isFinite_mulzero(data[2]) && isFinite_mulzero(data[3]);
201 }
202
203 #define mulzeroadd(data) (data[0]*0 + data[1]*0 + data[2]*0 + data[3]*0)
204
isfinite_plus_int(const float data[4])205 static bool isfinite_plus_int(const float data[4]) {
206 return isFinite_int(mulzeroadd(data));
207 }
208
isfinite_plus_mulzero(const float data[4])209 static bool isfinite_plus_mulzero(const float data[4]) {
210 float x = mulzeroadd(data);
211 return x == x;
212 }
213
214 typedef bool (*IsFiniteProc)(const float[]);
215
216 #define MAKEREC(name) { name, #name }
217
218 static const struct {
219 IsFiniteProc fProc;
220 const char* fName;
221 } gRec[] = {
222 MAKEREC(isfinite_and_int),
223 MAKEREC(isfinite_and_mulzero),
224 MAKEREC(isfinite_plus_int),
225 MAKEREC(isfinite_plus_mulzero),
226 };
227
228 #undef MAKEREC
229
isFinite(const SkRect & r)230 static bool isFinite(const SkRect& r) {
231 // x * 0 will be NaN iff x is infinity or NaN.
232 // a + b will be NaN iff either a or b is NaN.
233 float value = r.fLeft * 0 + r.fTop * 0 + r.fRight * 0 + r.fBottom * 0;
234
235 // value is either NaN or it is finite (zero).
236 // value==value will be true iff value is not NaN
237 return value == value;
238 }
239
240 class IsFiniteBench : public Benchmark {
241 enum {
242 N = 1000,
243 };
244 float fData[N];
245 public:
246
IsFiniteBench(int index)247 IsFiniteBench(int index) {
248 SkRandom rand;
249
250 for (int i = 0; i < N; ++i) {
251 fData[i] = rand.nextSScalar1();
252 }
253
254 if (index < 0) {
255 fProc = nullptr;
256 fName = "isfinite_rect";
257 } else {
258 fProc = gRec[index].fProc;
259 fName = gRec[index].fName;
260 }
261 }
262
isSuitableFor(Backend backend)263 bool isSuitableFor(Backend backend) override {
264 return backend == Backend::kNonRendering;
265 }
266
267 protected:
onDraw(int loops,SkCanvas *)268 void onDraw(int loops, SkCanvas*) override {
269 IsFiniteProc proc = fProc;
270 const float* data = fData;
271 // do this so the compiler won't throw away the function call
272 int counter = 0;
273
274 if (proc) {
275 for (int j = 0; j < loops; ++j) {
276 for (int i = 0; i < N - 4; ++i) {
277 counter += proc(&data[i]);
278 }
279 }
280 } else {
281 for (int j = 0; j < loops; ++j) {
282 for (int i = 0; i < N - 4; ++i) {
283 const SkRect* r = reinterpret_cast<const SkRect*>(&data[i]);
284 if ((false)) { // avoid bit rot, suppress warning
285 isFinite(*r);
286 }
287 counter += r->isFinite();
288 }
289 }
290 }
291
292 SkPaint paint;
293 if (paint.getAlpha() == 0) {
294 SkDebugf("%d\n", counter);
295 }
296 }
297
onGetName()298 const char* onGetName() override {
299 return fName;
300 }
301
302 private:
303 IsFiniteProc fProc;
304 const char* fName;
305
306 using INHERITED = Benchmark;
307 };
308
309 class FloorBench : public Benchmark {
310 enum {
311 ARRAY = 1000,
312 };
313 float fData[ARRAY];
314 bool fFast;
315 public:
316
FloorBench(bool fast)317 FloorBench(bool fast) : fFast(fast) {
318 SkRandom rand;
319
320 for (int i = 0; i < ARRAY; ++i) {
321 fData[i] = rand.nextSScalar1();
322 }
323
324 if (fast) {
325 fName = "floor_fast";
326 } else {
327 fName = "floor_std";
328 }
329 }
330
isSuitableFor(Backend backend)331 bool isSuitableFor(Backend backend) override {
332 return backend == Backend::kNonRendering;
333 }
334
process(float)335 virtual void process(float) {}
336
337 protected:
onDraw(int loops,SkCanvas *)338 void onDraw(int loops, SkCanvas*) override {
339 SkRandom rand;
340 float accum = 0;
341 const float* data = fData;
342
343 if (fFast) {
344 for (int j = 0; j < loops; ++j) {
345 for (int i = 0; i < ARRAY; ++i) {
346 accum += fast_floor(data[i]);
347 }
348 this->process(accum);
349 }
350 } else {
351 for (int j = 0; j < loops; ++j) {
352 for (int i = 0; i < ARRAY; ++i) {
353 accum += std::floor(data[i]);
354 }
355 this->process(accum);
356 }
357 }
358 }
359
onGetName()360 const char* onGetName() override {
361 return fName;
362 }
363
364 private:
365 const char* fName;
366
367 using INHERITED = Benchmark;
368 };
369
370 class CLZBench : public Benchmark {
371 enum {
372 ARRAY = 1000,
373 };
374 uint32_t fData[ARRAY];
375 bool fUsePortable;
376
377 public:
CLZBench(bool usePortable)378 CLZBench(bool usePortable) : fUsePortable(usePortable) {
379
380 SkRandom rand;
381 for (int i = 0; i < ARRAY; ++i) {
382 fData[i] = rand.nextU();
383 }
384
385 if (fUsePortable) {
386 fName = "clz_portable";
387 } else {
388 fName = "clz_intrinsic";
389 }
390 }
391
isSuitableFor(Backend backend)392 bool isSuitableFor(Backend backend) override {
393 return backend == Backend::kNonRendering;
394 }
395
396 // just so the compiler doesn't remove our loops
process(int)397 virtual void process(int) {}
398
399 protected:
onDraw(int loops,SkCanvas *)400 void onDraw(int loops, SkCanvas*) override {
401 int accum = 0;
402
403 if (fUsePortable) {
404 for (int j = 0; j < loops; ++j) {
405 for (int i = 0; i < ARRAY; ++i) {
406 accum += SkCLZ_portable(fData[i]);
407 }
408 this->process(accum);
409 }
410 } else {
411 for (int j = 0; j < loops; ++j) {
412 for (int i = 0; i < ARRAY; ++i) {
413 accum += SkCLZ(fData[i]);
414 }
415 this->process(accum);
416 }
417 }
418 }
419
onGetName()420 const char* onGetName() override {
421 return fName;
422 }
423
424 private:
425 const char* fName;
426
427 using INHERITED = Benchmark;
428 };
429
430 class CTZBench : public Benchmark {
431 enum {
432 ARRAY = 1000,
433 };
434 uint32_t fData[ARRAY];
435 bool fUsePortable;
436
437 public:
CTZBench(bool usePortable)438 CTZBench(bool usePortable) : fUsePortable(usePortable) {
439
440 SkRandom rand;
441 for (int i = 0; i < ARRAY; ++i) {
442 fData[i] = rand.nextU();
443 }
444
445 if (fUsePortable) {
446 fName = "ctz_portable";
447 } else {
448 fName = "ctz_intrinsic";
449 }
450 }
451
isSuitableFor(Backend backend)452 bool isSuitableFor(Backend backend) override {
453 return backend == Backend::kNonRendering;
454 }
455
456 // just so the compiler doesn't remove our loops
process(int)457 virtual void process(int) {}
458
459 protected:
onDraw(int loops,SkCanvas *)460 void onDraw(int loops, SkCanvas*) override {
461 int accum = 0;
462
463 if (fUsePortable) {
464 for (int j = 0; j < loops; ++j) {
465 for (int i = 0; i < ARRAY; ++i) {
466 accum += SkCTZ_portable(fData[i]);
467 }
468 this->process(accum);
469 }
470 } else {
471 for (int j = 0; j < loops; ++j) {
472 for (int i = 0; i < ARRAY; ++i) {
473 accum += SkCTZ(fData[i]);
474 }
475 this->process(accum);
476 }
477 }
478 }
479
onGetName()480 const char* onGetName() override {
481 return fName;
482 }
483
484 private:
485 const char* fName;
486
487 using INHERITED = Benchmark;
488 };
489
490 ///////////////////////////////////////////////////////////////////////////////
491
492 class NormalizeBench : public Benchmark {
493 enum {
494 ARRAY =1000,
495 };
496 SkVector fVec[ARRAY];
497
498 public:
NormalizeBench()499 NormalizeBench() {
500 SkRandom rand;
501 for (int i = 0; i < ARRAY; ++i) {
502 fVec[i].set(rand.nextSScalar1(), rand.nextSScalar1());
503 }
504
505 fName = "point_normalize";
506 }
507
isSuitableFor(Backend backend)508 bool isSuitableFor(Backend backend) override {
509 return backend == Backend::kNonRendering;
510 }
511
512 // just so the compiler doesn't remove our loops
process(int)513 virtual void process(int) {}
514
515 protected:
onDraw(int loops,SkCanvas *)516 void onDraw(int loops, SkCanvas*) override {
517 int accum = 0;
518
519 for (int j = 0; j < loops; ++j) {
520 for (int i = 0; i < ARRAY; ++i) {
521 accum += fVec[i].normalize();
522 }
523 this->process(accum);
524 }
525 }
526
onGetName()527 const char* onGetName() override {
528 return fName;
529 }
530
531 private:
532 const char* fName;
533
534 using INHERITED = Benchmark;
535 };
536
537 ///////////////////////////////////////////////////////////////////////////////
538
539 class FixedMathBench : public Benchmark {
540 enum {
541 N = 1000,
542 };
543 float fData[N];
544 SkFixed fResult[N];
545 public:
546
FixedMathBench()547 FixedMathBench() {
548 SkRandom rand;
549 for (int i = 0; i < N; ++i) {
550 fData[i] = rand.nextSScalar1();
551 }
552
553 }
554
isSuitableFor(Backend backend)555 bool isSuitableFor(Backend backend) override {
556 return backend == Backend::kNonRendering;
557 }
558
559 protected:
onDraw(int loops,SkCanvas *)560 void onDraw(int loops, SkCanvas*) override {
561 for (int j = 0; j < loops; ++j) {
562 for (int i = 0; i < N - 4; ++i) {
563 fResult[i] = SkFloatToFixed(fData[i]);
564 }
565 }
566
567 SkPaint paint;
568 if (paint.getAlpha() == 0) {
569 SkDebugf("%d\n", fResult[0]);
570 }
571 }
572
onGetName()573 const char* onGetName() override {
574 return "float_to_fixed";
575 }
576
577 private:
578 using INHERITED = Benchmark;
579 };
580
581 ///////////////////////////////////////////////////////////////////////////////
582
583 DEF_BENCH( return new NoOpMathBench(); )
584 DEF_BENCH( return new SkRSqrtMathBench(); )
585 DEF_BENCH( return new SlowISqrtMathBench(); )
586 DEF_BENCH( return new FastISqrtMathBench(); )
587 DEF_BENCH( return new QMul64Bench(); )
588 DEF_BENCH( return new QMul32Bench(); )
589
590 DEF_BENCH( return new IsFiniteBench(-1); )
591 DEF_BENCH( return new IsFiniteBench(0); )
592 DEF_BENCH( return new IsFiniteBench(1); )
593 DEF_BENCH( return new IsFiniteBench(2); )
594 DEF_BENCH( return new IsFiniteBench(3); )
595
596 DEF_BENCH( return new FloorBench(false); )
597 DEF_BENCH( return new FloorBench(true); )
598
599 DEF_BENCH( return new CLZBench(false); )
600 DEF_BENCH( return new CLZBench(true); )
601 DEF_BENCH( return new CTZBench(false); )
602 DEF_BENCH( return new CTZBench(true); )
603
604 DEF_BENCH( return new NormalizeBench(); )
605
606 DEF_BENCH( return new FixedMathBench(); )
607
608 //////////////////////////////////////////////////////////////
609
610 #include "src/base/SkFloatBits.h"
611 class Floor2IntBench : public Benchmark {
612 enum {
613 ARRAY = 1000,
614 };
615 float fData[ARRAY];
616 const bool fSat;
617 public:
618
Floor2IntBench(bool sat)619 Floor2IntBench(bool sat) : fSat(sat) {
620 SkRandom rand;
621
622 for (int i = 0; i < ARRAY; ++i) {
623 fData[i] = SkBits2Float(rand.nextU());
624 }
625
626 if (sat) {
627 fName = "floor2int_sat";
628 } else {
629 fName = "floor2int_undef";
630 }
631 }
632
isSuitableFor(Backend backend)633 bool isSuitableFor(Backend backend) override {
634 return backend == Backend::kNonRendering;
635 }
636
637 // These exist to try to stop the compiler from detecting what we doing, and throwing
638 // parts away (or knowing exactly how big the loop counts are).
process(unsigned)639 virtual void process(unsigned) {}
count()640 virtual int count() { return ARRAY; }
641
642 protected:
onDraw(int loops,SkCanvas *)643 void onDraw(int loops, SkCanvas*) override {
644 // used unsigned to avoid undefined behavior if/when the += might overflow
645 unsigned accum = 0;
646
647 for (int j = 0; j < loops; ++j) {
648 int n = this->count();
649 if (fSat) {
650 for (int i = 0; i < n; ++i) {
651 accum += sk_float_floor2int(fData[i]);
652 }
653 } else {
654 for (int i = 0; i < n; ++i) {
655 accum += sk_float_floor2int_no_saturate(fData[i]);
656 }
657 }
658 this->process(accum);
659 }
660 }
661
onGetName()662 const char* onGetName() override { return fName; }
663
664 private:
665 const char* fName;
666
667 using INHERITED = Benchmark;
668 };
669 DEF_BENCH( return new Floor2IntBench(false); )
670 DEF_BENCH( return new Floor2IntBench(true); )
671
672