In some cases the license-identification header was missing, so I added that as well. Replaced the remaining headers that specifically identified "Versions 3.13.7 and higher". Makefiles and the build system were deliberately excluded.
358 lines
10 KiB
C++
358 lines
10 KiB
C++
/*************************************************************************\
|
|
* 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;
|
|
}
|