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