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