MathBench.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  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. #include "bench/Benchmark.h"
  8. #include "include/core/SkMatrix.h"
  9. #include "include/core/SkPaint.h"
  10. #include "include/core/SkString.h"
  11. #include "include/private/SkColorData.h"
  12. #include "include/private/SkFixed.h"
  13. #include "include/utils/SkRandom.h"
  14. #include "src/core/SkMathPriv.h"
  15. static float sk_fsel(float pred, float result_ge, float result_lt) {
  16. return pred >= 0 ? result_ge : result_lt;
  17. }
  18. static float fast_floor(float x) {
  19. // float big = sk_fsel(x, 0x1.0p+23, -0x1.0p+23);
  20. float big = sk_fsel(x, (float)(1 << 23), -(float)(1 << 23));
  21. return (x + big) - big;
  22. }
  23. class MathBench : public Benchmark {
  24. enum {
  25. kBuffer = 100,
  26. };
  27. SkString fName;
  28. float fSrc[kBuffer], fDst[kBuffer];
  29. public:
  30. MathBench(const char name[]) {
  31. fName.printf("math_%s", name);
  32. SkRandom rand;
  33. for (int i = 0; i < kBuffer; ++i) {
  34. fSrc[i] = rand.nextSScalar1();
  35. }
  36. }
  37. bool isSuitableFor(Backend backend) override {
  38. return backend == kNonRendering_Backend;
  39. }
  40. virtual void performTest(float* SK_RESTRICT dst,
  41. const float* SK_RESTRICT src,
  42. int count) = 0;
  43. protected:
  44. virtual int mulLoopCount() const { return 1; }
  45. const char* onGetName() override {
  46. return fName.c_str();
  47. }
  48. void onDraw(int loops, SkCanvas*) override {
  49. int n = loops * this->mulLoopCount();
  50. for (int i = 0; i < n; i++) {
  51. this->performTest(fDst, fSrc, kBuffer);
  52. }
  53. }
  54. private:
  55. typedef Benchmark INHERITED;
  56. };
  57. class MathBenchU32 : public MathBench {
  58. public:
  59. MathBenchU32(const char name[]) : INHERITED(name) {}
  60. protected:
  61. virtual void performITest(uint32_t* SK_RESTRICT dst,
  62. const uint32_t* SK_RESTRICT src,
  63. int count) = 0;
  64. void performTest(float* SK_RESTRICT dst, const float* SK_RESTRICT src, int count) override {
  65. uint32_t* d = reinterpret_cast<uint32_t*>(dst);
  66. const uint32_t* s = reinterpret_cast<const uint32_t*>(src);
  67. this->performITest(d, s, count);
  68. }
  69. private:
  70. typedef MathBench INHERITED;
  71. };
  72. ///////////////////////////////////////////////////////////////////////////////
  73. class NoOpMathBench : public MathBench {
  74. public:
  75. NoOpMathBench() : INHERITED("noOp") {}
  76. protected:
  77. void performTest(float* SK_RESTRICT dst, const float* SK_RESTRICT src, int count) override {
  78. for (int i = 0; i < count; ++i) {
  79. dst[i] = src[i] + 1;
  80. }
  81. }
  82. private:
  83. typedef MathBench INHERITED;
  84. };
  85. class SkRSqrtMathBench : public MathBench {
  86. public:
  87. SkRSqrtMathBench() : INHERITED("sk_float_rsqrt") {}
  88. protected:
  89. void performTest(float* SK_RESTRICT dst, const float* SK_RESTRICT src, int count) override {
  90. for (int i = 0; i < count; ++i) {
  91. dst[i] = sk_float_rsqrt(src[i]);
  92. }
  93. }
  94. private:
  95. typedef MathBench INHERITED;
  96. };
  97. class SlowISqrtMathBench : public MathBench {
  98. public:
  99. SlowISqrtMathBench() : INHERITED("slowIsqrt") {}
  100. protected:
  101. void performTest(float* SK_RESTRICT dst, const float* SK_RESTRICT src, int count) override {
  102. for (int i = 0; i < count; ++i) {
  103. dst[i] = 1.0f / sk_float_sqrt(src[i]);
  104. }
  105. }
  106. private:
  107. typedef MathBench INHERITED;
  108. };
  109. class FastISqrtMathBench : public MathBench {
  110. public:
  111. FastISqrtMathBench() : INHERITED("fastIsqrt") {}
  112. protected:
  113. void performTest(float* SK_RESTRICT dst, const float* SK_RESTRICT src, int count) override {
  114. for (int i = 0; i < count; ++i) {
  115. dst[i] = sk_float_rsqrt(src[i]);
  116. }
  117. }
  118. private:
  119. typedef MathBench INHERITED;
  120. };
  121. static inline uint32_t QMul64(uint32_t value, U8CPU alpha) {
  122. SkASSERT((uint8_t)alpha == alpha);
  123. const uint32_t mask = 0xFF00FF;
  124. uint64_t tmp = value;
  125. tmp = (tmp & mask) | ((tmp & ~mask) << 24);
  126. tmp *= alpha;
  127. return (uint32_t) (((tmp >> 8) & mask) | ((tmp >> 32) & ~mask));
  128. }
  129. class QMul64Bench : public MathBenchU32 {
  130. public:
  131. QMul64Bench() : INHERITED("qmul64") {}
  132. protected:
  133. void performITest(uint32_t* SK_RESTRICT dst,
  134. const uint32_t* SK_RESTRICT src,
  135. int count) override {
  136. for (int i = 0; i < count; ++i) {
  137. dst[i] = QMul64(src[i], (uint8_t)i);
  138. }
  139. }
  140. private:
  141. typedef MathBenchU32 INHERITED;
  142. };
  143. class QMul32Bench : public MathBenchU32 {
  144. public:
  145. QMul32Bench() : INHERITED("qmul32") {}
  146. protected:
  147. void performITest(uint32_t* SK_RESTRICT dst,
  148. const uint32_t* SK_RESTRICT src,
  149. int count) override {
  150. for (int i = 0; i < count; ++i) {
  151. dst[i] = SkAlphaMulQ(src[i], (uint8_t)i);
  152. }
  153. }
  154. private:
  155. typedef MathBenchU32 INHERITED;
  156. };
  157. ///////////////////////////////////////////////////////////////////////////////
  158. static bool isFinite_int(float x) {
  159. uint32_t bits = SkFloat2Bits(x); // need unsigned for our shifts
  160. int exponent = bits << 1 >> 24;
  161. return exponent != 0xFF;
  162. }
  163. static bool isFinite_float(float x) {
  164. return SkToBool(sk_float_isfinite(x));
  165. }
  166. static bool isFinite_mulzero(float x) {
  167. float y = x * 0;
  168. return y == y;
  169. }
  170. static bool isfinite_and_int(const float data[4]) {
  171. return isFinite_int(data[0]) && isFinite_int(data[1]) && isFinite_int(data[2]) && isFinite_int(data[3]);
  172. }
  173. static bool isfinite_and_float(const float data[4]) {
  174. return isFinite_float(data[0]) && isFinite_float(data[1]) && isFinite_float(data[2]) && isFinite_float(data[3]);
  175. }
  176. static bool isfinite_and_mulzero(const float data[4]) {
  177. return isFinite_mulzero(data[0]) && isFinite_mulzero(data[1]) && isFinite_mulzero(data[2]) && isFinite_mulzero(data[3]);
  178. }
  179. #define mulzeroadd(data) (data[0]*0 + data[1]*0 + data[2]*0 + data[3]*0)
  180. static bool isfinite_plus_int(const float data[4]) {
  181. return isFinite_int(mulzeroadd(data));
  182. }
  183. static bool isfinite_plus_float(const float data[4]) {
  184. return !sk_float_isnan(mulzeroadd(data));
  185. }
  186. static bool isfinite_plus_mulzero(const float data[4]) {
  187. float x = mulzeroadd(data);
  188. return x == x;
  189. }
  190. typedef bool (*IsFiniteProc)(const float[]);
  191. #define MAKEREC(name) { name, #name }
  192. static const struct {
  193. IsFiniteProc fProc;
  194. const char* fName;
  195. } gRec[] = {
  196. MAKEREC(isfinite_and_int),
  197. MAKEREC(isfinite_and_float),
  198. MAKEREC(isfinite_and_mulzero),
  199. MAKEREC(isfinite_plus_int),
  200. MAKEREC(isfinite_plus_float),
  201. MAKEREC(isfinite_plus_mulzero),
  202. };
  203. #undef MAKEREC
  204. static bool isFinite(const SkRect& r) {
  205. // x * 0 will be NaN iff x is infinity or NaN.
  206. // a + b will be NaN iff either a or b is NaN.
  207. float value = r.fLeft * 0 + r.fTop * 0 + r.fRight * 0 + r.fBottom * 0;
  208. // value is either NaN or it is finite (zero).
  209. // value==value will be true iff value is not NaN
  210. return value == value;
  211. }
  212. class IsFiniteBench : public Benchmark {
  213. enum {
  214. N = 1000,
  215. };
  216. float fData[N];
  217. public:
  218. IsFiniteBench(int index) {
  219. SkRandom rand;
  220. for (int i = 0; i < N; ++i) {
  221. fData[i] = rand.nextSScalar1();
  222. }
  223. if (index < 0) {
  224. fProc = nullptr;
  225. fName = "isfinite_rect";
  226. } else {
  227. fProc = gRec[index].fProc;
  228. fName = gRec[index].fName;
  229. }
  230. }
  231. bool isSuitableFor(Backend backend) override {
  232. return backend == kNonRendering_Backend;
  233. }
  234. protected:
  235. void onDraw(int loops, SkCanvas*) override {
  236. IsFiniteProc proc = fProc;
  237. const float* data = fData;
  238. // do this so the compiler won't throw away the function call
  239. int counter = 0;
  240. if (proc) {
  241. for (int j = 0; j < loops; ++j) {
  242. for (int i = 0; i < N - 4; ++i) {
  243. counter += proc(&data[i]);
  244. }
  245. }
  246. } else {
  247. for (int j = 0; j < loops; ++j) {
  248. for (int i = 0; i < N - 4; ++i) {
  249. const SkRect* r = reinterpret_cast<const SkRect*>(&data[i]);
  250. if (false) { // avoid bit rot, suppress warning
  251. isFinite(*r);
  252. }
  253. counter += r->isFinite();
  254. }
  255. }
  256. }
  257. SkPaint paint;
  258. if (paint.getAlpha() == 0) {
  259. SkDebugf("%d\n", counter);
  260. }
  261. }
  262. const char* onGetName() override {
  263. return fName;
  264. }
  265. private:
  266. IsFiniteProc fProc;
  267. const char* fName;
  268. typedef Benchmark INHERITED;
  269. };
  270. class FloorBench : public Benchmark {
  271. enum {
  272. ARRAY = 1000,
  273. };
  274. float fData[ARRAY];
  275. bool fFast;
  276. public:
  277. FloorBench(bool fast) : fFast(fast) {
  278. SkRandom rand;
  279. for (int i = 0; i < ARRAY; ++i) {
  280. fData[i] = rand.nextSScalar1();
  281. }
  282. if (fast) {
  283. fName = "floor_fast";
  284. } else {
  285. fName = "floor_std";
  286. }
  287. }
  288. bool isSuitableFor(Backend backend) override {
  289. return backend == kNonRendering_Backend;
  290. }
  291. virtual void process(float) {}
  292. protected:
  293. void onDraw(int loops, SkCanvas*) override {
  294. SkRandom rand;
  295. float accum = 0;
  296. const float* data = fData;
  297. if (fFast) {
  298. for (int j = 0; j < loops; ++j) {
  299. for (int i = 0; i < ARRAY; ++i) {
  300. accum += fast_floor(data[i]);
  301. }
  302. this->process(accum);
  303. }
  304. } else {
  305. for (int j = 0; j < loops; ++j) {
  306. for (int i = 0; i < ARRAY; ++i) {
  307. accum += sk_float_floor(data[i]);
  308. }
  309. this->process(accum);
  310. }
  311. }
  312. }
  313. const char* onGetName() override {
  314. return fName;
  315. }
  316. private:
  317. const char* fName;
  318. typedef Benchmark INHERITED;
  319. };
  320. class CLZBench : public Benchmark {
  321. enum {
  322. ARRAY = 1000,
  323. };
  324. uint32_t fData[ARRAY];
  325. bool fUsePortable;
  326. public:
  327. CLZBench(bool usePortable) : fUsePortable(usePortable) {
  328. SkRandom rand;
  329. for (int i = 0; i < ARRAY; ++i) {
  330. fData[i] = rand.nextU();
  331. }
  332. if (fUsePortable) {
  333. fName = "clz_portable";
  334. } else {
  335. fName = "clz_intrinsic";
  336. }
  337. }
  338. bool isSuitableFor(Backend backend) override {
  339. return backend == kNonRendering_Backend;
  340. }
  341. // just so the compiler doesn't remove our loops
  342. virtual void process(int) {}
  343. protected:
  344. void onDraw(int loops, SkCanvas*) override {
  345. int accum = 0;
  346. if (fUsePortable) {
  347. for (int j = 0; j < loops; ++j) {
  348. for (int i = 0; i < ARRAY; ++i) {
  349. accum += SkCLZ_portable(fData[i]);
  350. }
  351. this->process(accum);
  352. }
  353. } else {
  354. for (int j = 0; j < loops; ++j) {
  355. for (int i = 0; i < ARRAY; ++i) {
  356. accum += SkCLZ(fData[i]);
  357. }
  358. this->process(accum);
  359. }
  360. }
  361. }
  362. const char* onGetName() override {
  363. return fName;
  364. }
  365. private:
  366. const char* fName;
  367. typedef Benchmark INHERITED;
  368. };
  369. ///////////////////////////////////////////////////////////////////////////////
  370. class NormalizeBench : public Benchmark {
  371. enum {
  372. ARRAY =1000,
  373. };
  374. SkVector fVec[ARRAY];
  375. public:
  376. NormalizeBench() {
  377. SkRandom rand;
  378. for (int i = 0; i < ARRAY; ++i) {
  379. fVec[i].set(rand.nextSScalar1(), rand.nextSScalar1());
  380. }
  381. fName = "point_normalize";
  382. }
  383. bool isSuitableFor(Backend backend) override {
  384. return backend == kNonRendering_Backend;
  385. }
  386. // just so the compiler doesn't remove our loops
  387. virtual void process(int) {}
  388. protected:
  389. void onDraw(int loops, SkCanvas*) override {
  390. int accum = 0;
  391. for (int j = 0; j < loops; ++j) {
  392. for (int i = 0; i < ARRAY; ++i) {
  393. accum += fVec[i].normalize();
  394. }
  395. this->process(accum);
  396. }
  397. }
  398. const char* onGetName() override {
  399. return fName;
  400. }
  401. private:
  402. const char* fName;
  403. typedef Benchmark INHERITED;
  404. };
  405. ///////////////////////////////////////////////////////////////////////////////
  406. class FixedMathBench : public Benchmark {
  407. enum {
  408. N = 1000,
  409. };
  410. float fData[N];
  411. SkFixed fResult[N];
  412. public:
  413. FixedMathBench() {
  414. SkRandom rand;
  415. for (int i = 0; i < N; ++i) {
  416. fData[i] = rand.nextSScalar1();
  417. }
  418. }
  419. bool isSuitableFor(Backend backend) override {
  420. return backend == kNonRendering_Backend;
  421. }
  422. protected:
  423. void onDraw(int loops, SkCanvas*) override {
  424. for (int j = 0; j < loops; ++j) {
  425. for (int i = 0; i < N - 4; ++i) {
  426. fResult[i] = SkFloatToFixed(fData[i]);
  427. }
  428. }
  429. SkPaint paint;
  430. if (paint.getAlpha() == 0) {
  431. SkDebugf("%d\n", fResult[0]);
  432. }
  433. }
  434. const char* onGetName() override {
  435. return "float_to_fixed";
  436. }
  437. private:
  438. typedef Benchmark INHERITED;
  439. };
  440. ///////////////////////////////////////////////////////////////////////////////
  441. template <typename T>
  442. class DivModBench : public Benchmark {
  443. SkString fName;
  444. public:
  445. explicit DivModBench(const char* name) {
  446. fName.printf("divmod_%s", name);
  447. }
  448. bool isSuitableFor(Backend backend) override {
  449. return backend == kNonRendering_Backend;
  450. }
  451. protected:
  452. const char* onGetName() override {
  453. return fName.c_str();
  454. }
  455. void onDraw(int loops, SkCanvas*) override {
  456. volatile T a = 0, b = 0;
  457. T div = 0, mod = 0;
  458. for (int i = 0; i < loops; i++) {
  459. if ((T)i == 0) continue; // Small T will wrap around.
  460. SkTDivMod((T)(i+1), (T)i, &div, &mod);
  461. a ^= div;
  462. b ^= mod;
  463. }
  464. }
  465. };
  466. DEF_BENCH(return new DivModBench<uint8_t>("uint8_t"))
  467. DEF_BENCH(return new DivModBench<uint16_t>("uint16_t"))
  468. DEF_BENCH(return new DivModBench<uint32_t>("uint32_t"))
  469. DEF_BENCH(return new DivModBench<uint64_t>("uint64_t"))
  470. DEF_BENCH(return new DivModBench<int8_t>("int8_t"))
  471. DEF_BENCH(return new DivModBench<int16_t>("int16_t"))
  472. DEF_BENCH(return new DivModBench<int32_t>("int32_t"))
  473. DEF_BENCH(return new DivModBench<int64_t>("int64_t"))
  474. ///////////////////////////////////////////////////////////////////////////////
  475. DEF_BENCH( return new NoOpMathBench(); )
  476. DEF_BENCH( return new SkRSqrtMathBench(); )
  477. DEF_BENCH( return new SlowISqrtMathBench(); )
  478. DEF_BENCH( return new FastISqrtMathBench(); )
  479. DEF_BENCH( return new QMul64Bench(); )
  480. DEF_BENCH( return new QMul32Bench(); )
  481. DEF_BENCH( return new IsFiniteBench(-1); )
  482. DEF_BENCH( return new IsFiniteBench(0); )
  483. DEF_BENCH( return new IsFiniteBench(1); )
  484. DEF_BENCH( return new IsFiniteBench(2); )
  485. DEF_BENCH( return new IsFiniteBench(3); )
  486. DEF_BENCH( return new IsFiniteBench(4); )
  487. DEF_BENCH( return new IsFiniteBench(5); )
  488. DEF_BENCH( return new FloorBench(false); )
  489. DEF_BENCH( return new FloorBench(true); )
  490. DEF_BENCH( return new CLZBench(false); )
  491. DEF_BENCH( return new CLZBench(true); )
  492. DEF_BENCH( return new NormalizeBench(); )
  493. DEF_BENCH( return new FixedMathBench(); )
  494. //////////////////////////////////////////////////////////////
  495. #include "include/private/SkFloatBits.h"
  496. class Floor2IntBench : public Benchmark {
  497. enum {
  498. ARRAY = 1000,
  499. };
  500. float fData[ARRAY];
  501. const bool fSat;
  502. public:
  503. Floor2IntBench(bool sat) : fSat(sat) {
  504. SkRandom rand;
  505. for (int i = 0; i < ARRAY; ++i) {
  506. fData[i] = SkBits2Float(rand.nextU());
  507. }
  508. if (sat) {
  509. fName = "floor2int_sat";
  510. } else {
  511. fName = "floor2int_undef";
  512. }
  513. }
  514. bool isSuitableFor(Backend backend) override {
  515. return backend == kNonRendering_Backend;
  516. }
  517. // These exist to try to stop the compiler from detecting what we doing, and throwing
  518. // parts away (or knowing exactly how big the loop counts are).
  519. virtual void process(unsigned) {}
  520. virtual int count() { return ARRAY; }
  521. protected:
  522. void onDraw(int loops, SkCanvas*) override {
  523. // used unsigned to avoid undefined behavior if/when the += might overflow
  524. unsigned accum = 0;
  525. for (int j = 0; j < loops; ++j) {
  526. int n = this->count();
  527. if (fSat) {
  528. for (int i = 0; i < n; ++i) {
  529. accum += sk_float_floor2int(fData[i]);
  530. }
  531. } else {
  532. for (int i = 0; i < n; ++i) {
  533. accum += sk_float_floor2int_no_saturate(fData[i]);
  534. }
  535. }
  536. this->process(accum);
  537. }
  538. }
  539. const char* onGetName() override { return fName; }
  540. private:
  541. const char* fName;
  542. typedef Benchmark INHERITED;
  543. };
  544. DEF_BENCH( return new Floor2IntBench(false); )
  545. DEF_BENCH( return new Floor2IntBench(true); )