xref: /aosp_15_r20/external/skia/bench/MathBench.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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"
9 #include "include/core/SkMatrix.h"
10 #include "include/core/SkPaint.h"
11 #include "include/core/SkString.h"
12 #include "include/private/SkColorData.h"
13 #include "include/private/base/SkFixed.h"
14 #include "src/base/SkMathPriv.h"
15 #include "src/base/SkRandom.h"
16 
sk_fsel(float pred,float result_ge,float result_lt)17 static float sk_fsel(float pred, float result_ge, float result_lt) {
18     return pred >= 0 ? result_ge : result_lt;
19 }
20 
fast_floor(float x)21 static 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 
27 class MathBench : public Benchmark {
28     enum {
29         kBuffer = 100,
30     };
31     SkString    fName;
32     float       fSrc[kBuffer], fDst[kBuffer];
33 public:
MathBench(const char name[])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 
isSuitableFor(Backend backend)43     bool isSuitableFor(Backend backend) override {
44         return backend == Backend::kNonRendering;
45     }
46 
47     virtual void performTest(float* SK_RESTRICT dst,
48                               const float* SK_RESTRICT src,
49                               int count) = 0;
50 
51 protected:
mulLoopCount() const52     virtual int mulLoopCount() const { return 1; }
53 
onGetName()54     const char* onGetName() override {
55         return fName.c_str();
56     }
57 
onDraw(int loops,SkCanvas *)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 
65 private:
66     using INHERITED = Benchmark;
67 };
68 
69 class MathBenchU32 : public MathBench {
70 public:
MathBenchU32(const char name[])71     MathBenchU32(const char name[]) : INHERITED(name) {}
72 
73 protected:
74     virtual void performITest(uint32_t* SK_RESTRICT dst,
75                               const uint32_t* SK_RESTRICT src,
76                               int count) = 0;
77 
performTest(float * SK_RESTRICT dst,const float * SK_RESTRICT src,int count)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     }
83 private:
84     using INHERITED = MathBench;
85 };
86 
87 ///////////////////////////////////////////////////////////////////////////////
88 
89 class NoOpMathBench : public MathBench {
90 public:
NoOpMathBench()91     NoOpMathBench() : INHERITED("noOp") {}
92 protected:
performTest(float * SK_RESTRICT dst,const float * SK_RESTRICT src,int count)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     }
98 private:
99     using INHERITED = MathBench;
100 };
101 
102 class SkRSqrtMathBench : public MathBench {
103 public:
SkRSqrtMathBench()104     SkRSqrtMathBench() : INHERITED("sk_float_rsqrt") {}
105 protected:
performTest(float * SK_RESTRICT dst,const float * SK_RESTRICT src,int count)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     }
111 private:
112     using INHERITED = MathBench;
113 };
114 
115 
116 class SlowISqrtMathBench : public MathBench {
117 public:
SlowISqrtMathBench()118     SlowISqrtMathBench() : INHERITED("slowIsqrt") {}
119 protected:
performTest(float * SK_RESTRICT dst,const float * SK_RESTRICT src,int count)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     }
125 private:
126     using INHERITED = MathBench;
127 };
128 
129 class FastISqrtMathBench : public MathBench {
130 public:
FastISqrtMathBench()131     FastISqrtMathBench() : INHERITED("fastIsqrt") {}
132 protected:
performTest(float * SK_RESTRICT dst,const float * SK_RESTRICT src,int count)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     }
138 private:
139     using INHERITED = MathBench;
140 };
141 
QMul64(uint32_t value,U8CPU alpha)142 static 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 
152 class QMul64Bench : public MathBenchU32 {
153 public:
QMul64Bench()154     QMul64Bench() : INHERITED("qmul64") {}
155 protected:
performITest(uint32_t * SK_RESTRICT dst,const uint32_t * SK_RESTRICT src,int count)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     }
163 private:
164     using INHERITED = MathBenchU32;
165 };
166 
167 class QMul32Bench : public MathBenchU32 {
168 public:
QMul32Bench()169     QMul32Bench() : INHERITED("qmul32") {}
170 protected:
performITest(uint32_t * SK_RESTRICT dst,const uint32_t * SK_RESTRICT src,int count)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     }
178 private:
179     using INHERITED = MathBenchU32;
180 };
181 
182 ///////////////////////////////////////////////////////////////////////////////
183 
isFinite_int(float x)184 static 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 
isFinite_mulzero(float x)190 static bool isFinite_mulzero(float x) {
191     float y = x * 0;
192     return y == y;
193 }
194 
isfinite_and_int(const float data[4])195 static 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 
isfinite_and_mulzero(const float data[4])199 static 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 
isfinite_plus_int(const float data[4])205 static bool isfinite_plus_int(const float data[4]) {
206     return  isFinite_int(mulzeroadd(data));
207 }
208 
isfinite_plus_mulzero(const float data[4])209 static bool isfinite_plus_mulzero(const float data[4]) {
210     float x = mulzeroadd(data);
211     return x == x;
212 }
213 
214 typedef bool (*IsFiniteProc)(const float[]);
215 
216 #define MAKEREC(name)   { name, #name }
217 
218 static const struct {
219     IsFiniteProc    fProc;
220     const char*     fName;
221 } gRec[] = {
222     MAKEREC(isfinite_and_int),
223     MAKEREC(isfinite_and_mulzero),
224     MAKEREC(isfinite_plus_int),
225     MAKEREC(isfinite_plus_mulzero),
226 };
227 
228 #undef MAKEREC
229 
isFinite(const SkRect & r)230 static 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 
240 class IsFiniteBench : public Benchmark {
241     enum {
242         N = 1000,
243     };
244     float fData[N];
245 public:
246 
IsFiniteBench(int index)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 
isSuitableFor(Backend backend)263     bool isSuitableFor(Backend backend) override {
264         return backend == Backend::kNonRendering;
265     }
266 
267 protected:
onDraw(int loops,SkCanvas *)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 
292         SkPaint paint;
293         if (paint.getAlpha() == 0) {
294             SkDebugf("%d\n", counter);
295         }
296     }
297 
onGetName()298     const char* onGetName() override {
299         return fName;
300     }
301 
302 private:
303     IsFiniteProc    fProc;
304     const char*     fName;
305 
306     using INHERITED = Benchmark;
307 };
308 
309 class FloorBench : public Benchmark {
310     enum {
311         ARRAY = 1000,
312     };
313     float fData[ARRAY];
314     bool fFast;
315 public:
316 
FloorBench(bool fast)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 
isSuitableFor(Backend backend)331     bool isSuitableFor(Backend backend) override {
332         return backend == Backend::kNonRendering;
333     }
334 
process(float)335     virtual void process(float) {}
336 
337 protected:
onDraw(int loops,SkCanvas *)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 
onGetName()360     const char* onGetName() override {
361         return fName;
362     }
363 
364 private:
365     const char*     fName;
366 
367     using INHERITED = Benchmark;
368 };
369 
370 class CLZBench : public Benchmark {
371     enum {
372         ARRAY = 1000,
373     };
374     uint32_t fData[ARRAY];
375     bool fUsePortable;
376 
377 public:
CLZBench(bool usePortable)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 
isSuitableFor(Backend backend)392     bool isSuitableFor(Backend backend) override {
393         return backend == Backend::kNonRendering;
394     }
395 
396     // just so the compiler doesn't remove our loops
process(int)397     virtual void process(int) {}
398 
399 protected:
onDraw(int loops,SkCanvas *)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 
onGetName()420     const char* onGetName() override {
421         return fName;
422     }
423 
424 private:
425     const char* fName;
426 
427     using INHERITED = Benchmark;
428 };
429 
430 class CTZBench : public Benchmark {
431     enum {
432         ARRAY = 1000,
433     };
434     uint32_t fData[ARRAY];
435     bool fUsePortable;
436 
437 public:
CTZBench(bool usePortable)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 
isSuitableFor(Backend backend)452     bool isSuitableFor(Backend backend) override {
453         return backend == Backend::kNonRendering;
454     }
455 
456     // just so the compiler doesn't remove our loops
process(int)457     virtual void process(int) {}
458 
459 protected:
onDraw(int loops,SkCanvas *)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 
onGetName()480     const char* onGetName() override {
481         return fName;
482     }
483 
484 private:
485     const char* fName;
486 
487     using INHERITED = Benchmark;
488 };
489 
490 ///////////////////////////////////////////////////////////////////////////////
491 
492 class NormalizeBench : public Benchmark {
493     enum {
494         ARRAY =1000,
495     };
496     SkVector fVec[ARRAY];
497 
498 public:
NormalizeBench()499     NormalizeBench() {
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 
isSuitableFor(Backend backend)508     bool isSuitableFor(Backend backend) override {
509         return backend == Backend::kNonRendering;
510     }
511 
512     // just so the compiler doesn't remove our loops
process(int)513     virtual void process(int) {}
514 
515 protected:
onDraw(int loops,SkCanvas *)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 
onGetName()527     const char* onGetName() override {
528         return fName;
529     }
530 
531 private:
532     const char* fName;
533 
534     using INHERITED = Benchmark;
535 };
536 
537 ///////////////////////////////////////////////////////////////////////////////
538 
539 class FixedMathBench : public Benchmark {
540     enum {
541         N = 1000,
542     };
543     float fData[N];
544     SkFixed fResult[N];
545 public:
546 
FixedMathBench()547     FixedMathBench()  {
548         SkRandom rand;
549         for (int i = 0; i < N; ++i) {
550             fData[i] = rand.nextSScalar1();
551         }
552 
553     }
554 
isSuitableFor(Backend backend)555     bool isSuitableFor(Backend backend) override {
556         return backend == Backend::kNonRendering;
557     }
558 
559 protected:
onDraw(int loops,SkCanvas *)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 
567         SkPaint paint;
568         if (paint.getAlpha() == 0) {
569             SkDebugf("%d\n", fResult[0]);
570         }
571     }
572 
onGetName()573     const char* onGetName() override {
574         return "float_to_fixed";
575     }
576 
577 private:
578     using INHERITED = Benchmark;
579 };
580 
581 ///////////////////////////////////////////////////////////////////////////////
582 
583 DEF_BENCH( return new NoOpMathBench(); )
584 DEF_BENCH( return new SkRSqrtMathBench(); )
585 DEF_BENCH( return new SlowISqrtMathBench(); )
586 DEF_BENCH( return new FastISqrtMathBench(); )
587 DEF_BENCH( return new QMul64Bench(); )
588 DEF_BENCH( return new QMul32Bench(); )
589 
590 DEF_BENCH( return new IsFiniteBench(-1); )
591 DEF_BENCH( return new IsFiniteBench(0); )
592 DEF_BENCH( return new IsFiniteBench(1); )
593 DEF_BENCH( return new IsFiniteBench(2); )
594 DEF_BENCH( return new IsFiniteBench(3); )
595 
596 DEF_BENCH( return new FloorBench(false); )
597 DEF_BENCH( return new FloorBench(true); )
598 
599 DEF_BENCH( return new CLZBench(false); )
600 DEF_BENCH( return new CLZBench(true); )
601 DEF_BENCH( return new CTZBench(false); )
602 DEF_BENCH( return new CTZBench(true); )
603 
604 DEF_BENCH( return new NormalizeBench(); )
605 
606 DEF_BENCH( return new FixedMathBench(); )
607 
608 //////////////////////////////////////////////////////////////
609 
610 #include "src/base/SkFloatBits.h"
611 class Floor2IntBench : public Benchmark {
612     enum {
613         ARRAY = 1000,
614     };
615     float fData[ARRAY];
616     const bool fSat;
617 public:
618 
Floor2IntBench(bool sat)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 
isSuitableFor(Backend backend)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).
process(unsigned)639     virtual void process(unsigned) {}
count()640     virtual int count() { return ARRAY; }
641 
642 protected:
onDraw(int loops,SkCanvas *)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 
onGetName()662     const char* onGetName() override { return fName; }
663 
664 private:
665     const char* fName;
666 
667     using INHERITED = Benchmark;
668 };
669 DEF_BENCH( return new Floor2IntBench(false); )
670 DEF_BENCH( return new Floor2IntBench(true); )
671 
672