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 17static float sk_fsel(float pred, float result_ge, float result_lt) { 18 return pred >= 0 ? result_ge : result_lt; 19} 20 21static 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 27class MathBench : public Benchmark { 28 enum { 29 kBuffer = 100, 30 }; 31 SkString fName; 32 float fSrc[kBuffer], fDst[kBuffer]; 33public: 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 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 51protected: 52 virtual int mulLoopCount() const { return 1; } 53 54 const char* onGetName() override { 55 return fName.c_str(); 56 } 57 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 65private: 66 typedef Benchmark INHERITED; 67}; 68 69class MathBenchU32 : public MathBench { 70public: 71 MathBenchU32(const char name[]) : INHERITED(name) {} 72 73protected: 74 virtual void performITest(uint32_t* SK_RESTRICT dst, 75 const uint32_t* SK_RESTRICT src, 76 int count) = 0; 77 78 void performTest(float* SK_RESTRICT dst, const float* SK_RESTRICT src, int count) override { 79 uint32_t* d = SkTCast<uint32_t*>(dst); 80 const uint32_t* s = SkTCast<const uint32_t*>(src); 81 this->performITest(d, s, count); 82 } 83private: 84 typedef MathBench INHERITED; 85}; 86 87/////////////////////////////////////////////////////////////////////////////// 88 89class NoOpMathBench : public MathBench { 90public: 91 NoOpMathBench() : INHERITED("noOp") {} 92protected: 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 } 98private: 99 typedef MathBench INHERITED; 100}; 101 102class SkRSqrtMathBench : public MathBench { 103public: 104 SkRSqrtMathBench() : INHERITED("sk_float_rsqrt") {} 105protected: 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 } 111private: 112 typedef MathBench INHERITED; 113}; 114 115 116class SlowISqrtMathBench : public MathBench { 117public: 118 SlowISqrtMathBench() : INHERITED("slowIsqrt") {} 119protected: 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 } 125private: 126 typedef MathBench INHERITED; 127}; 128 129class FastISqrtMathBench : public MathBench { 130public: 131 FastISqrtMathBench() : INHERITED("fastIsqrt") {} 132protected: 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 } 138private: 139 typedef MathBench INHERITED; 140}; 141 142static 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 152class QMul64Bench : public MathBenchU32 { 153public: 154 QMul64Bench() : INHERITED("qmul64") {} 155protected: 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 } 163private: 164 typedef MathBenchU32 INHERITED; 165}; 166 167class QMul32Bench : public MathBenchU32 { 168public: 169 QMul32Bench() : INHERITED("qmul32") {} 170protected: 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 } 178private: 179 typedef MathBenchU32 INHERITED; 180}; 181 182/////////////////////////////////////////////////////////////////////////////// 183 184static 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 190static bool isFinite_float(float x) { 191 return SkToBool(sk_float_isfinite(x)); 192} 193 194static bool isFinite_mulzero(float x) { 195 float y = x * 0; 196 return y == y; 197} 198 199static 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 203static 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 207static 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 213static bool isfinite_plus_int(const float data[4]) { 214 return isFinite_int(mulzeroadd(data)); 215} 216 217static bool isfinite_plus_float(const float data[4]) { 218 return !sk_float_isnan(mulzeroadd(data)); 219} 220 221static bool isfinite_plus_mulzero(const float data[4]) { 222 float x = mulzeroadd(data); 223 return x == x; 224} 225 226typedef bool (*IsFiniteProc)(const float[]); 227 228#define MAKEREC(name) { name, #name } 229 230static 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 244static 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 254class IsFiniteBench : public Benchmark { 255 enum { 256 N = 1000, 257 }; 258 float fData[N]; 259public: 260 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 277 bool isSuitableFor(Backend backend) override { 278 return backend == kNonRendering_Backend; 279 } 280 281protected: 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 312 const char* onGetName() override { 313 return fName; 314 } 315 316private: 317 IsFiniteProc fProc; 318 const char* fName; 319 320 typedef Benchmark INHERITED; 321}; 322 323class FloorBench : public Benchmark { 324 enum { 325 ARRAY = 1000, 326 }; 327 float fData[ARRAY]; 328 bool fFast; 329public: 330 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 345 bool isSuitableFor(Backend backend) override { 346 return backend == kNonRendering_Backend; 347 } 348 349 virtual void process(float) {} 350 351protected: 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 374 const char* onGetName() override { 375 return fName; 376 } 377 378private: 379 const char* fName; 380 381 typedef Benchmark INHERITED; 382}; 383 384class CLZBench : public Benchmark { 385 enum { 386 ARRAY = 1000, 387 }; 388 uint32_t fData[ARRAY]; 389 bool fUsePortable; 390 391public: 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 406 bool isSuitableFor(Backend backend) override { 407 return backend == kNonRendering_Backend; 408 } 409 410 // just so the compiler doesn't remove our loops 411 virtual void process(int) {} 412 413protected: 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 434 const char* onGetName() override { 435 return fName; 436 } 437 438private: 439 const char* fName; 440 441 typedef Benchmark INHERITED; 442}; 443 444/////////////////////////////////////////////////////////////////////////////// 445 446class NormalizeBench : public Benchmark { 447 enum { 448 ARRAY =1000, 449 }; 450 SkVector fVec[ARRAY]; 451 452public: 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 462 bool isSuitableFor(Backend backend) override { 463 return backend == kNonRendering_Backend; 464 } 465 466 // just so the compiler doesn't remove our loops 467 virtual void process(int) {} 468 469protected: 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 481 const char* onGetName() override { 482 return fName; 483 } 484 485private: 486 const char* fName; 487 488 typedef Benchmark INHERITED; 489}; 490 491/////////////////////////////////////////////////////////////////////////////// 492 493class FixedMathBench : public Benchmark { 494 enum { 495 N = 1000, 496 }; 497 float fData[N]; 498 SkFixed fResult[N]; 499public: 500 501 FixedMathBench() { 502 SkRandom rand; 503 for (int i = 0; i < N; ++i) { 504 fData[i] = rand.nextSScalar1(); 505 } 506 507 } 508 509 bool isSuitableFor(Backend backend) override { 510 return backend == kNonRendering_Backend; 511 } 512 513protected: 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 527 const char* onGetName() override { 528 return "float_to_fixed"; 529 } 530 531private: 532 typedef Benchmark INHERITED; 533}; 534 535/////////////////////////////////////////////////////////////////////////////// 536 537template <typename T> 538class DivModBench : public Benchmark { 539 SkString fName; 540public: 541 explicit DivModBench(const char* name) { 542 fName.printf("divmod_%s", name); 543 } 544 545 bool isSuitableFor(Backend backend) override { 546 return backend == kNonRendering_Backend; 547 } 548 549protected: 550 const char* onGetName() override { 551 return fName.c_str(); 552 } 553 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}; 565DEF_BENCH(return new DivModBench<uint8_t>("uint8_t")) 566DEF_BENCH(return new DivModBench<uint16_t>("uint16_t")) 567DEF_BENCH(return new DivModBench<uint32_t>("uint32_t")) 568DEF_BENCH(return new DivModBench<uint64_t>("uint64_t")) 569 570DEF_BENCH(return new DivModBench<int8_t>("int8_t")) 571DEF_BENCH(return new DivModBench<int16_t>("int16_t")) 572DEF_BENCH(return new DivModBench<int32_t>("int32_t")) 573DEF_BENCH(return new DivModBench<int64_t>("int64_t")) 574 575/////////////////////////////////////////////////////////////////////////////// 576 577DEF_BENCH( return new NoOpMathBench(); ) 578DEF_BENCH( return new SkRSqrtMathBench(); ) 579DEF_BENCH( return new SlowISqrtMathBench(); ) 580DEF_BENCH( return new FastISqrtMathBench(); ) 581DEF_BENCH( return new QMul64Bench(); ) 582DEF_BENCH( return new QMul32Bench(); ) 583 584DEF_BENCH( return new IsFiniteBench(-1); ) 585DEF_BENCH( return new IsFiniteBench(0); ) 586DEF_BENCH( return new IsFiniteBench(1); ) 587DEF_BENCH( return new IsFiniteBench(2); ) 588DEF_BENCH( return new IsFiniteBench(3); ) 589DEF_BENCH( return new IsFiniteBench(4); ) 590DEF_BENCH( return new IsFiniteBench(5); ) 591 592DEF_BENCH( return new FloorBench(false); ) 593DEF_BENCH( return new FloorBench(true); ) 594 595DEF_BENCH( return new CLZBench(false); ) 596DEF_BENCH( return new CLZBench(true); ) 597 598DEF_BENCH( return new NormalizeBench(); ) 599 600DEF_BENCH( return new FixedMathBench(); ) 601 602////////////////////////////////////////////////////////////// 603 604#include "../private/SkFloatBits.h" 605class Floor2IntBench : public Benchmark { 606 enum { 607 ARRAY = 1000, 608 }; 609 float fData[ARRAY]; 610 const bool fSat; 611public: 612 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 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). 633 virtual void process(unsigned) {} 634 virtual int count() { return ARRAY; } 635 636protected: 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 656 const char* onGetName() override { return fName; } 657 658private: 659 const char* fName; 660 661 typedef Benchmark INHERITED; 662}; 663DEF_BENCH( return new Floor2IntBench(false); ) 664DEF_BENCH( return new Floor2IntBench(true); ) 665 666