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