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