/modules/libcom/test/cvtFastPerform.cpp
https://github.com/epics-base/epics-base · C++ · 357 lines · 292 code · 53 blank · 12 comment · 20 complexity · db6593bd7017a0837eb7e18b1aacb55d MD5 · raw file
- /*************************************************************************\
- * SPDX-License-Identifier: EPICS
- * EPICS BASE is distributed subject to a Software License Agreement found
- * in file LICENSE that is included with this distribution.
- \*************************************************************************/
- // Original Author: Jeff Hill, LANL
- #include <cmath>
- #include <cfloat>
- #include <cstdlib>
- #include <cstring>
- #include <stdexcept>
- #include <typeinfo>
- #include <iostream>
- #include "epicsStdio.h"
- #include "cvtFast.h"
- #include "epicsTime.h"
- #include "testMain.h"
- using namespace std;
- class PerfConverter {
- public:
- virtual const char * name(void) const = 0;
- virtual int maxPrecision(void) const = 0;
- virtual void target (double srcD, float srcF, char *dst, size_t len, int prec) const = 0;
- virtual void add(int prec, double elapsed) = 0;
- virtual double total(int prec) = 0;
- virtual ~PerfConverter () {};
- };
- class Perf {
- public:
- Perf ( int maxConverters );
- virtual ~Perf ();
- void addConverter( PerfConverter * c );
- void execute (int count, bool verbose);
- void report (const char *title, int count);
- protected:
- static unsigned const nUnrolled = 10;
- static const unsigned uSecPerSec = 1000000;
- static unsigned const nIterations = 10000;
- const int maxConverters;
- PerfConverter **converters;
- int nConverters;
- int maxPrecision;
- bool verbose;
- void measure ( double srcD, float srcF, int prec );
- private:
- Perf ( const Perf & );
- Perf & operator = ( Perf & );
- };
- Perf :: Perf ( int maxConverters_ ) :
- maxConverters ( maxConverters_ ),
- converters ( new PerfConverter * [ maxConverters_ ] ),
- nConverters ( 0 ),
- maxPrecision ( 0 )
- {
- }
- Perf :: ~Perf ()
- {
- for ( int j = 0; j < nConverters; j++ )
- delete converters[ j ];
- delete [] converters;
- }
- void Perf :: addConverter(PerfConverter *c)
- {
- if ( nConverters >= maxConverters )
- throw std :: runtime_error ( "Too many converters" );
- converters[ nConverters++ ] = c;
- int prec = c->maxPrecision();
- if ( prec > maxPrecision )
- maxPrecision = prec;
- }
- void Perf :: execute (const int count, bool verbose_)
- {
- verbose = verbose_;
- for ( int i = 0; i < count; i++ ) {
- double srcDbl = rand ();
- srcDbl /= (RAND_MAX + 1.0);
- srcDbl *= 20.0;
- srcDbl -= 10.0;
- float srcFlt = (float) srcDbl;
- for ( int prec = 0; prec <= maxPrecision; prec++ ) {
- measure (srcFlt, srcDbl, prec);
- }
- }
- report ( "Small numbers, -10..+10", count );
- for ( int i = 0; i < count; i++ ) {
- double mVal = rand ();
- mVal /= (RAND_MAX + 1.0);
- double eVal = rand ();
- eVal /= (RAND_MAX + 1.0);
- double dVal = eVal;
- dVal *= FLT_MAX_EXP - FLT_MIN_EXP;
- dVal += FLT_MIN_EXP;
- int dEVal = static_cast < int > ( dVal + 0.5 );
- double srcDbl = ldexp ( mVal, dEVal );
- float srcFlt = (float) srcDbl;
- for ( int prec = 0; prec <= maxPrecision; prec++ ) {
- measure (srcFlt, srcDbl, prec);
- }
- }
- report ( "Random mantissa+exponent", count );
- }
- void Perf :: report (const char *title, const int count)
- {
- printf( "\n%s\n\nprec\t", title );
- for ( int j = 0; j < nConverters; j++ )
- printf( "%-16s ", converters[j]->name() );
- for (int prec = 0; prec <= maxPrecision; prec++ ) {
- printf( "\n %2d\t", prec );
- for (int j = 0; j < nConverters; j++ ) {
- PerfConverter *c = converters[j];
- if (prec > c->maxPrecision())
- printf( "%11s ", "-" );
- else {
- printf( "%11.9f sec ", c->total(prec) / count );
- }
- }
- }
- printf( "\n\n" );
- }
- void Perf :: measure (double srcD, float srcF, int prec)
- {
- char buf[40];
- for ( int j = 0; j < nConverters; j++ ) {
- PerfConverter *c = converters[j];
- if (prec > c->maxPrecision())
- continue;
- std::memset(buf, 0, sizeof(buf));
- epicsTime beg = epicsTime :: getMonotonic ();
- for ( unsigned i = 0; i < nIterations; i++ ) {
- c->target (srcD, srcF, buf, sizeof(buf) - 1, prec);
- }
- epicsTime end = epicsTime :: getMonotonic ();
- double elapsed = end - beg;
- elapsed /= nIterations * nUnrolled;
- c->add( prec, elapsed );
- if (verbose)
- printf ( "%17s: %11.9f sec, prec=%2i '%s'\n",
- c->name (), elapsed, prec, buf );
- }
- }
- // Conversions to be measured
- class PerfCvtFastFloat : public PerfConverter {
- static const int digits = 12;
- public:
- PerfCvtFastFloat ()
- {
- for (int i = 0; i <= digits; i++)
- measured[i] = 0; // Some targets seem to need this
- }
- int maxPrecision (void) const { return digits; }
- const char *name (void) const { return "cvtFloatToString"; }
- void target (double srcD, float srcF, char *dst, size_t len, int prec) const
- {
- cvtFloatToString ( srcF, dst, prec );
- cvtFloatToString ( srcF, dst, prec );
- cvtFloatToString ( srcF, dst, prec );
- cvtFloatToString ( srcF, dst, prec );
- cvtFloatToString ( srcF, dst, prec );
- cvtFloatToString ( srcF, dst, prec );
- cvtFloatToString ( srcF, dst, prec );
- cvtFloatToString ( srcF, dst, prec );
- cvtFloatToString ( srcF, dst, prec );
- cvtFloatToString ( srcF, dst, prec );
- }
- void add (int prec, double elapsed) { measured[prec] += elapsed; }
- double total (int prec) {
- double total = measured[prec];
- measured[prec] = 0;
- return total;
- }
- private:
- double measured[digits+1];
- };
- class PerfCvtFastDouble : public PerfConverter {
- static const int digits = 17;
- public:
- PerfCvtFastDouble ()
- {
- for (int i = 0; i <= digits; i++)
- measured[i] = 0; // Some targets seem to need this
- }
- int maxPrecision (void) const { return digits; }
- const char *name (void) const { return "cvtDoubleToString"; }
- void target (double srcD, float srcF, char *dst, size_t len, int prec) const
- {
- cvtDoubleToString ( srcD, dst, prec );
- cvtDoubleToString ( srcD, dst, prec );
- cvtDoubleToString ( srcD, dst, prec );
- cvtDoubleToString ( srcD, dst, prec );
- cvtDoubleToString ( srcD, dst, prec );
- cvtDoubleToString ( srcD, dst, prec );
- cvtDoubleToString ( srcD, dst, prec );
- cvtDoubleToString ( srcD, dst, prec );
- cvtDoubleToString ( srcD, dst, prec );
- cvtDoubleToString ( srcD, dst, prec );
- }
- void add(int prec, double elapsed) { measured[prec] += elapsed; }
- double total (int prec) {
- double total = measured[prec];
- measured[prec] = 0;
- return total;
- }
- private:
- double measured[digits+1];
- };
- class PerfSNPrintf : public PerfConverter {
- static const int digits = 17;
- public:
- PerfSNPrintf ()
- {
- for (int i = 0; i <= digits; i++)
- measured[i] = 0; // Some targets seem to need this
- }
- int maxPrecision (void) const { return digits; }
- const char *name (void) const { return "epicsSnprintf"; }
- void target (double srcD, float srcF, char *dst, size_t len, int prec) const
- {
- epicsSnprintf ( dst, len, "%.*g", prec, srcD );
- epicsSnprintf ( dst, len, "%.*g", prec, srcD );
- epicsSnprintf ( dst, len, "%.*g", prec, srcD );
- epicsSnprintf ( dst, len, "%.*g", prec, srcD );
- epicsSnprintf ( dst, len, "%.*g", prec, srcD );
- epicsSnprintf ( dst, len, "%.*g", prec, srcD );
- epicsSnprintf ( dst, len, "%.*g", prec, srcD );
- epicsSnprintf ( dst, len, "%.*g", prec, srcD );
- epicsSnprintf ( dst, len, "%.*g", prec, srcD );
- epicsSnprintf ( dst, len, "%.*g", prec, srcD );
- }
- void add(int prec, double elapsed) { measured[prec] += elapsed; }
- double total (int prec) {
- double total = measured[prec];
- measured[prec] = 0;
- return total;
- }
- private:
- double measured[digits+1];
- };
- // This is a quick-and-dirty std::streambuf converter that writes directly
- // into the output buffer. Performance is slower than epicsSnprintf().
- struct membuf: public std::streambuf {
- membuf(char *array, size_t size) {
- this->setp(array, array + size - 1);
- }
- };
- struct omemstream: virtual membuf, std::ostream {
- omemstream(char *array, size_t size):
- membuf(array, size),
- std::ostream(this) {
- }
- };
- static void ossConvertD(char *dst, size_t len, int prec, double src) {
- omemstream oss(dst, len);
- oss.precision(prec);
- oss << src << ends;
- }
- class PerfStreamBuf : public PerfConverter {
- static const int digits = 17;
- public:
- PerfStreamBuf ()
- {
- for (int i = 0; i <= digits; i++)
- measured[i] = 0; // Some targets seem to need this
- }
- int maxPrecision (void) const { return digits; }
- const char *name (void) const { return "std::streambuf"; }
- void target (double srcD, float srcF, char *dst, size_t len, int prec) const
- {
- ossConvertD ( dst, len, prec, srcD );
- ossConvertD ( dst, len, prec, srcD );
- ossConvertD ( dst, len, prec, srcD );
- ossConvertD ( dst, len, prec, srcD );
- ossConvertD ( dst, len, prec, srcD );
- ossConvertD ( dst, len, prec, srcD );
- ossConvertD ( dst, len, prec, srcD );
- ossConvertD ( dst, len, prec, srcD );
- ossConvertD ( dst, len, prec, srcD );
- ossConvertD ( dst, len, prec, srcD );
- }
- void add(int prec, double elapsed) { measured[prec] += elapsed; }
- double total (int prec) {
- double total = measured[prec];
- measured[prec] = 0;
- return total;
- }
- private:
- double measured[digits+1];
- };
- MAIN(cvtFastPerform)
- {
- Perf t(4);
- t.addConverter( new PerfCvtFastFloat );
- t.addConverter( new PerfCvtFastDouble );
- t.addConverter( new PerfSNPrintf );
- t.addConverter( new PerfStreamBuf );
- // The parameter to execute() below are:
- // count = number of different random numbers to measure
- // verbose = whether to display individual measurements
- #ifdef vxWorks
- t.execute (3, true); // Slow...
- #else
- t.execute (5, false);
- #endif
- return 0;
- }