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