Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
MathBench.cpp
Go to the documentation of this file.
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 "bench/Benchmark.h"
14#include "src/base/SkMathPriv.h"
15#include "src/base/SkRandom.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 {
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 using INHERITED = Benchmark;
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 = reinterpret_cast<uint32_t*>(dst);
80 const uint32_t* s = reinterpret_cast<const uint32_t*>(src);
81 this->performITest(d, s, count);
82 }
83private:
84 using INHERITED = MathBench;
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 using INHERITED = MathBench;
100};
101
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 using INHERITED = MathBench;
113};
114
115
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 / std::sqrt(src[i]);
123 }
124 }
125private:
126 using INHERITED = MathBench;
127};
128
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 using INHERITED = MathBench;
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 using INHERITED = MathBenchU32;
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 using INHERITED = MathBenchU32;
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_mulzero(float x) {
191 float y = x * 0;
192 return y == y;
193}
194
195static bool isfinite_and_int(const float data[4]) {
196 return isFinite_int(data[0]) && isFinite_int(data[1]) && isFinite_int(data[2]) && isFinite_int(data[3]);
197}
198
199static bool isfinite_and_mulzero(const float data[4]) {
200 return isFinite_mulzero(data[0]) && isFinite_mulzero(data[1]) && isFinite_mulzero(data[2]) && isFinite_mulzero(data[3]);
201}
202
203#define mulzeroadd(data) (data[0]*0 + data[1]*0 + data[2]*0 + data[3]*0)
204
205static bool isfinite_plus_int(const float data[4]) {
206 return isFinite_int(mulzeroadd(data));
207}
208
209static bool isfinite_plus_mulzero(const float data[4]) {
210 float x = mulzeroadd(data);
211 return x == x;
212}
213
214typedef bool (*IsFiniteProc)(const float[]);
215
216#define MAKEREC(name) { name, #name }
217
218static const struct {
220 const char* fName;
221} gRec[] = {
227
228#undef MAKEREC
229
230static bool isFinite(const SkRect& r) {
231 // x * 0 will be NaN iff x is infinity or NaN.
232 // a + b will be NaN iff either a or b is NaN.
233 float value = r.fLeft * 0 + r.fTop * 0 + r.fRight * 0 + r.fBottom * 0;
234
235 // value is either NaN or it is finite (zero).
236 // value==value will be true iff value is not NaN
237 return value == value;
238}
239
240class IsFiniteBench : public Benchmark {
241 enum {
242 N = 1000,
243 };
244 float fData[N];
245public:
246
247 IsFiniteBench(int index) {
248 SkRandom rand;
249
250 for (int i = 0; i < N; ++i) {
251 fData[i] = rand.nextSScalar1();
252 }
253
254 if (index < 0) {
255 fProc = nullptr;
256 fName = "isfinite_rect";
257 } else {
258 fProc = gRec[index].fProc;
259 fName = gRec[index].fName;
260 }
261 }
262
263 bool isSuitableFor(Backend backend) override {
265 }
266
267protected:
268 void onDraw(int loops, SkCanvas*) override {
269 IsFiniteProc proc = fProc;
270 const float* data = fData;
271 // do this so the compiler won't throw away the function call
272 int counter = 0;
273
274 if (proc) {
275 for (int j = 0; j < loops; ++j) {
276 for (int i = 0; i < N - 4; ++i) {
277 counter += proc(&data[i]);
278 }
279 }
280 } else {
281 for (int j = 0; j < loops; ++j) {
282 for (int i = 0; i < N - 4; ++i) {
283 const SkRect* r = reinterpret_cast<const SkRect*>(&data[i]);
284 if ((false)) { // avoid bit rot, suppress warning
285 isFinite(*r);
286 }
287 counter += r->isFinite();
288 }
289 }
290 }
291
293 if (paint.getAlpha() == 0) {
294 SkDebugf("%d\n", counter);
295 }
296 }
297
298 const char* onGetName() override {
299 return fName;
300 }
301
302private:
303 IsFiniteProc fProc;
304 const char* fName;
305
306 using INHERITED = Benchmark;
307};
308
309class FloorBench : public Benchmark {
310 enum {
311 ARRAY = 1000,
312 };
313 float fData[ARRAY];
314 bool fFast;
315public:
316
317 FloorBench(bool fast) : fFast(fast) {
318 SkRandom rand;
319
320 for (int i = 0; i < ARRAY; ++i) {
321 fData[i] = rand.nextSScalar1();
322 }
323
324 if (fast) {
325 fName = "floor_fast";
326 } else {
327 fName = "floor_std";
328 }
329 }
330
331 bool isSuitableFor(Backend backend) override {
333 }
334
335 virtual void process(float) {}
336
337protected:
338 void onDraw(int loops, SkCanvas*) override {
339 SkRandom rand;
340 float accum = 0;
341 const float* data = fData;
342
343 if (fFast) {
344 for (int j = 0; j < loops; ++j) {
345 for (int i = 0; i < ARRAY; ++i) {
346 accum += fast_floor(data[i]);
347 }
348 this->process(accum);
349 }
350 } else {
351 for (int j = 0; j < loops; ++j) {
352 for (int i = 0; i < ARRAY; ++i) {
353 accum += std::floor(data[i]);
354 }
355 this->process(accum);
356 }
357 }
358 }
359
360 const char* onGetName() override {
361 return fName;
362 }
363
364private:
365 const char* fName;
366
367 using INHERITED = Benchmark;
368};
369
370class CLZBench : public Benchmark {
371 enum {
372 ARRAY = 1000,
373 };
374 uint32_t fData[ARRAY];
375 bool fUsePortable;
376
377public:
378 CLZBench(bool usePortable) : fUsePortable(usePortable) {
379
380 SkRandom rand;
381 for (int i = 0; i < ARRAY; ++i) {
382 fData[i] = rand.nextU();
383 }
384
385 if (fUsePortable) {
386 fName = "clz_portable";
387 } else {
388 fName = "clz_intrinsic";
389 }
390 }
391
392 bool isSuitableFor(Backend backend) override {
394 }
395
396 // just so the compiler doesn't remove our loops
397 virtual void process(int) {}
398
399protected:
400 void onDraw(int loops, SkCanvas*) override {
401 int accum = 0;
402
403 if (fUsePortable) {
404 for (int j = 0; j < loops; ++j) {
405 for (int i = 0; i < ARRAY; ++i) {
406 accum += SkCLZ_portable(fData[i]);
407 }
408 this->process(accum);
409 }
410 } else {
411 for (int j = 0; j < loops; ++j) {
412 for (int i = 0; i < ARRAY; ++i) {
413 accum += SkCLZ(fData[i]);
414 }
415 this->process(accum);
416 }
417 }
418 }
419
420 const char* onGetName() override {
421 return fName;
422 }
423
424private:
425 const char* fName;
426
427 using INHERITED = Benchmark;
428};
429
430class CTZBench : public Benchmark {
431 enum {
432 ARRAY = 1000,
433 };
434 uint32_t fData[ARRAY];
435 bool fUsePortable;
436
437public:
438 CTZBench(bool usePortable) : fUsePortable(usePortable) {
439
440 SkRandom rand;
441 for (int i = 0; i < ARRAY; ++i) {
442 fData[i] = rand.nextU();
443 }
444
445 if (fUsePortable) {
446 fName = "ctz_portable";
447 } else {
448 fName = "ctz_intrinsic";
449 }
450 }
451
452 bool isSuitableFor(Backend backend) override {
454 }
455
456 // just so the compiler doesn't remove our loops
457 virtual void process(int) {}
458
459protected:
460 void onDraw(int loops, SkCanvas*) override {
461 int accum = 0;
462
463 if (fUsePortable) {
464 for (int j = 0; j < loops; ++j) {
465 for (int i = 0; i < ARRAY; ++i) {
466 accum += SkCTZ_portable(fData[i]);
467 }
468 this->process(accum);
469 }
470 } else {
471 for (int j = 0; j < loops; ++j) {
472 for (int i = 0; i < ARRAY; ++i) {
473 accum += SkCTZ(fData[i]);
474 }
475 this->process(accum);
476 }
477 }
478 }
479
480 const char* onGetName() override {
481 return fName;
482 }
483
484private:
485 const char* fName;
486
487 using INHERITED = Benchmark;
488};
489
490///////////////////////////////////////////////////////////////////////////////
491
492class NormalizeBench : public Benchmark {
493 enum {
494 ARRAY =1000,
495 };
496 SkVector fVec[ARRAY];
497
498public:
500 SkRandom rand;
501 for (int i = 0; i < ARRAY; ++i) {
502 fVec[i].set(rand.nextSScalar1(), rand.nextSScalar1());
503 }
504
505 fName = "point_normalize";
506 }
507
508 bool isSuitableFor(Backend backend) override {
510 }
511
512 // just so the compiler doesn't remove our loops
513 virtual void process(int) {}
514
515protected:
516 void onDraw(int loops, SkCanvas*) override {
517 int accum = 0;
518
519 for (int j = 0; j < loops; ++j) {
520 for (int i = 0; i < ARRAY; ++i) {
521 accum += fVec[i].normalize();
522 }
523 this->process(accum);
524 }
525 }
526
527 const char* onGetName() override {
528 return fName;
529 }
530
531private:
532 const char* fName;
533
534 using INHERITED = Benchmark;
535};
536
537///////////////////////////////////////////////////////////////////////////////
538
539class FixedMathBench : public Benchmark {
540 enum {
541 N = 1000,
542 };
543 float fData[N];
544 SkFixed fResult[N];
545public:
546
548 SkRandom rand;
549 for (int i = 0; i < N; ++i) {
550 fData[i] = rand.nextSScalar1();
551 }
552
553 }
554
555 bool isSuitableFor(Backend backend) override {
557 }
558
559protected:
560 void onDraw(int loops, SkCanvas*) override {
561 for (int j = 0; j < loops; ++j) {
562 for (int i = 0; i < N - 4; ++i) {
563 fResult[i] = SkFloatToFixed(fData[i]);
564 }
565 }
566
568 if (paint.getAlpha() == 0) {
569 SkDebugf("%d\n", fResult[0]);
570 }
571 }
572
573 const char* onGetName() override {
574 return "float_to_fixed";
575 }
576
577private:
578 using INHERITED = Benchmark;
579};
580
581///////////////////////////////////////////////////////////////////////////////
582
583DEF_BENCH( return new NoOpMathBench(); )
584DEF_BENCH( return new SkRSqrtMathBench(); )
585DEF_BENCH( return new SlowISqrtMathBench(); )
586DEF_BENCH( return new FastISqrtMathBench(); )
587DEF_BENCH( return new QMul64Bench(); )
588DEF_BENCH( return new QMul32Bench(); )
589
590DEF_BENCH( return new IsFiniteBench(-1); )
591DEF_BENCH( return new IsFiniteBench(0); )
592DEF_BENCH( return new IsFiniteBench(1); )
593DEF_BENCH( return new IsFiniteBench(2); )
594DEF_BENCH( return new IsFiniteBench(3); )
595
596DEF_BENCH( return new FloorBench(false); )
597DEF_BENCH( return new FloorBench(true); )
598
599DEF_BENCH( return new CLZBench(false); )
600DEF_BENCH( return new CLZBench(true); )
601DEF_BENCH( return new CTZBench(false); )
602DEF_BENCH( return new CTZBench(true); )
603
604DEF_BENCH( return new NormalizeBench(); )
605
606DEF_BENCH( return new FixedMathBench(); )
607
608//////////////////////////////////////////////////////////////
609
610#include "src/base/SkFloatBits.h"
611class Floor2IntBench : public Benchmark {
612 enum {
613 ARRAY = 1000,
614 };
615 float fData[ARRAY];
616 const bool fSat;
617public:
618
619 Floor2IntBench(bool sat) : fSat(sat) {
620 SkRandom rand;
621
622 for (int i = 0; i < ARRAY; ++i) {
623 fData[i] = SkBits2Float(rand.nextU());
624 }
625
626 if (sat) {
627 fName = "floor2int_sat";
628 } else {
629 fName = "floor2int_undef";
630 }
631 }
632
633 bool isSuitableFor(Backend backend) override {
634 return backend == Backend::kNonRendering;
635 }
636
637 // These exist to try to stop the compiler from detecting what we doing, and throwing
638 // parts away (or knowing exactly how big the loop counts are).
639 virtual void process(unsigned) {}
640 virtual int count() { return ARRAY; }
641
642protected:
643 void onDraw(int loops, SkCanvas*) override {
644 // used unsigned to avoid undefined behavior if/when the += might overflow
645 unsigned accum = 0;
646
647 for (int j = 0; j < loops; ++j) {
648 int n = this->count();
649 if (fSat) {
650 for (int i = 0; i < n; ++i) {
651 accum += sk_float_floor2int(fData[i]);
652 }
653 } else {
654 for (int i = 0; i < n; ++i) {
655 accum += sk_float_floor2int_no_saturate(fData[i]);
656 }
657 }
658 this->process(accum);
659 }
660 }
661
662 const char* onGetName() override { return fName; }
663
664private:
665 const char* fName;
666
667 using INHERITED = Benchmark;
668};
669DEF_BENCH( return new Floor2IntBench(false); )
670DEF_BENCH( return new Floor2IntBench(true); )
671
#define DEF_BENCH(code)
Definition Benchmark.h:20
const char * backend
const char * fName
int count
static bool isFinite_mulzero(float x)
static bool isFinite_int(float x)
static bool isfinite_plus_int(const float data[4])
static const struct @230 gRec[]
static float fast_floor(float x)
Definition MathBench.cpp:21
static bool isfinite_and_int(const float data[4])
static bool isfinite_and_mulzero(const float data[4])
#define mulzeroadd(data)
static bool isFinite(const SkRect &r)
static uint32_t QMul64(uint32_t value, U8CPU alpha)
#define MAKEREC(name)
static float sk_fsel(float pred, float result_ge, float result_lt)
Definition MathBench.cpp:17
const char * fName
IsFiniteProc fProc
static bool isfinite_plus_mulzero(const float data[4])
bool(* IsFiniteProc)(const float[])
#define SkASSERT(cond)
Definition SkAssert.h:116
unsigned U8CPU
Definition SkCPUTypes.h:18
static SK_ALWAYS_INLINE uint32_t SkAlphaMulQ(uint32_t c, unsigned scale)
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define SK_RESTRICT
Definition SkFeatures.h:42
int32_t SkFixed
Definition SkFixed.h:25
#define SkFloatToFixed(x)
Definition SkFixed.h:42
static float SkBits2Float(uint32_t bits)
Definition SkFloatBits.h:48
static uint32_t SkFloat2Bits(float value)
Definition SkFloatBits.h:41
#define sk_float_floor2int_no_saturate(x)
static float sk_float_rsqrt(float x)
#define sk_float_floor2int(x)
constexpr int SkCTZ_portable(uint32_t x)
Returns the number of trailing zero bits (0...32)
Definition SkMathPriv.h:193
static int SkCTZ(uint32_t mask)
Definition SkMathPriv.h:224
constexpr int SkCLZ_portable(uint32_t x)
Returns the number of leading zero bits (0...32)
Definition SkMathPriv.h:149
static int SkCLZ(uint32_t mask)
Definition SkMathPriv.h:186
#define INHERITED(method,...)
virtual void process(int)
bool isSuitableFor(Backend backend) override
const char * onGetName() override
void onDraw(int loops, SkCanvas *) override
CLZBench(bool usePortable)
bool isSuitableFor(Backend backend) override
virtual void process(int)
const char * onGetName() override
CTZBench(bool usePortable)
void onDraw(int loops, SkCanvas *) override
void performTest(float *SK_RESTRICT dst, const float *SK_RESTRICT src, int count) override
const char * onGetName() override
void onDraw(int loops, SkCanvas *) override
bool isSuitableFor(Backend backend) override
void onDraw(int loops, SkCanvas *) override
virtual int count()
virtual void process(unsigned)
bool isSuitableFor(Backend backend) override
const char * onGetName() override
Floor2IntBench(bool sat)
FloorBench(bool fast)
const char * onGetName() override
virtual void process(float)
bool isSuitableFor(Backend backend) override
void onDraw(int loops, SkCanvas *) override
void onDraw(int loops, SkCanvas *) override
const char * onGetName() override
IsFiniteBench(int index)
bool isSuitableFor(Backend backend) override
void performTest(float *SK_RESTRICT dst, const float *SK_RESTRICT src, int count) override
Definition MathBench.cpp:78
virtual void performITest(uint32_t *SK_RESTRICT dst, const uint32_t *SK_RESTRICT src, int count)=0
MathBenchU32(const char name[])
Definition MathBench.cpp:71
MathBench(const char name[])
Definition MathBench.cpp:34
bool isSuitableFor(Backend backend) override
Definition MathBench.cpp:43
virtual void performTest(float *SK_RESTRICT dst, const float *SK_RESTRICT src, int count)=0
void onDraw(int loops, SkCanvas *) override
Definition MathBench.cpp:58
const char * onGetName() override
Definition MathBench.cpp:54
virtual int mulLoopCount() const
Definition MathBench.cpp:52
void performTest(float *SK_RESTRICT dst, const float *SK_RESTRICT src, int count) override
Definition MathBench.cpp:93
void onDraw(int loops, SkCanvas *) override
const char * onGetName() override
bool isSuitableFor(Backend backend) override
virtual void process(int)
void performITest(uint32_t *SK_RESTRICT dst, const uint32_t *SK_RESTRICT src, int count) override
void performITest(uint32_t *SK_RESTRICT dst, const uint32_t *SK_RESTRICT src, int count) override
void performTest(float *SK_RESTRICT dst, const float *SK_RESTRICT src, int count) override
uint32_t nextU()
Definition SkRandom.h:42
SkScalar nextSScalar1()
Definition SkRandom.h:113
void performTest(float *SK_RESTRICT dst, const float *SK_RESTRICT src, int count) override
const Paint & paint
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
struct MyStruct s
uint8_t value
const char * name
Definition fuchsia.cc:50
static float sat(float r, float g, float b)
Definition hsl.cpp:51
double y
double x
static void process(const char *inPath, const char *lexer, const char *token, const char *hPath, const char *cppPath)
Definition Main.cpp:114
void set(float x, float y)
bool normalize()
Definition SkPoint.cpp:22
SkScalar fBottom
larger y-axis bounds
Definition extension.cpp:17
bool isFinite() const
Definition SkRect.h:711
SkScalar fLeft
smaller x-axis bounds
Definition extension.cpp:14
SkScalar fRight
larger x-axis bounds
Definition extension.cpp:16
SkScalar fTop
smaller y-axis bounds
Definition extension.cpp:15