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 8 static float sk_fsel(float pred, float result_ge, float result_lt) { 9 return pred >= 0 ? result_ge : result_lt; 10 } 11 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: 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 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: 43 virtual int mulLoopCount() const { return 1; } 44 45 virtual const char* onGetName() { 46 return fName.c_str(); 47 } 48 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: 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 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: 84 NoOpMathBench() : INHERITED("noOp") {} 85 protected: 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: 99 SkRSqrtMathBench() : INHERITED("sk_float_rsqrt") {} 100 protected: 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: 115 SlowISqrtMathBench() : INHERITED("slowIsqrt") {} 116 protected: 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 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: 140 FastISqrtMathBench() : INHERITED("fastIsqrt") {} 141 protected: 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 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: 165 QMul64Bench() : INHERITED("qmul64") {} 166 protected: 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: 180 QMul32Bench() : INHERITED("qmul32") {} 181 protected: 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 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 201 static bool isFinite_float(float x) { 202 return SkToBool(sk_float_isfinite(x)); 203 } 204 205 static bool isFinite_mulzero(float x) { 206 float y = x * 0; 207 return y == y; 208 } 209 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 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 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 224 static bool isfinite_plus_int(const float data[4]) { 225 return isFinite_int(mulzeroadd(data)); 226 } 227 228 static bool isfinite_plus_float(const float data[4]) { 229 return !sk_float_isnan(mulzeroadd(data)); 230 } 231 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 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 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 288 virtual bool isSuitableFor(Backend backend) SK_OVERRIDE { 289 return backend == kNonRendering_Backend; 290 } 291 292 protected: 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 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 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 356 virtual bool isSuitableFor(Backend backend) SK_OVERRIDE { 357 return backend == kNonRendering_Backend; 358 } 359 360 virtual void process(float) {} 361 362 protected: 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 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: 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 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 422 virtual void process(int) {} 423 424 protected: 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 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: 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 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 478 virtual void process(int) {} 479 480 protected: 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 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 512 FixedMathBench() { 513 SkRandom rand; 514 for (int i = 0; i < N; ++i) { 515 fData[i] = rand.nextSScalar1(); 516 } 517 518 } 519 520 virtual bool isSuitableFor(Backend backend) SK_OVERRIDE { 521 return backend == kNonRendering_Backend; 522 } 523 524 protected: 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 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: 552 explicit DivModBench(const char* name) { 553 fName.printf("divmod_%s", name); 554 } 555 556 virtual bool isSuitableFor(Backend backend) SK_OVERRIDE { 557 return backend == kNonRendering_Backend; 558 } 559 560 protected: 561 virtual const char* onGetName() { 562 return fName.c_str(); 563 } 564 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