/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

  1. /*************************************************************************\
  2. * SPDX-License-Identifier: EPICS
  3. * EPICS BASE is distributed subject to a Software License Agreement found
  4. * in file LICENSE that is included with this distribution.
  5. \*************************************************************************/
  6. // Original Author: Jeff Hill, LANL
  7. #include <cmath>
  8. #include <cfloat>
  9. #include <cstdlib>
  10. #include <cstring>
  11. #include <stdexcept>
  12. #include <typeinfo>
  13. #include <iostream>
  14. #include "epicsStdio.h"
  15. #include "cvtFast.h"
  16. #include "epicsTime.h"
  17. #include "testMain.h"
  18. using namespace std;
  19. class PerfConverter {
  20. public:
  21. virtual const char * name(void) const = 0;
  22. virtual int maxPrecision(void) const = 0;
  23. virtual void target (double srcD, float srcF, char *dst, size_t len, int prec) const = 0;
  24. virtual void add(int prec, double elapsed) = 0;
  25. virtual double total(int prec) = 0;
  26. virtual ~PerfConverter () {};
  27. };
  28. class Perf {
  29. public:
  30. Perf ( int maxConverters );
  31. virtual ~Perf ();
  32. void addConverter( PerfConverter * c );
  33. void execute (int count, bool verbose);
  34. void report (const char *title, int count);
  35. protected:
  36. static unsigned const nUnrolled = 10;
  37. static const unsigned uSecPerSec = 1000000;
  38. static unsigned const nIterations = 10000;
  39. const int maxConverters;
  40. PerfConverter **converters;
  41. int nConverters;
  42. int maxPrecision;
  43. bool verbose;
  44. void measure ( double srcD, float srcF, int prec );
  45. private:
  46. Perf ( const Perf & );
  47. Perf & operator = ( Perf & );
  48. };
  49. Perf :: Perf ( int maxConverters_ ) :
  50. maxConverters ( maxConverters_ ),
  51. converters ( new PerfConverter * [ maxConverters_ ] ),
  52. nConverters ( 0 ),
  53. maxPrecision ( 0 )
  54. {
  55. }
  56. Perf :: ~Perf ()
  57. {
  58. for ( int j = 0; j < nConverters; j++ )
  59. delete converters[ j ];
  60. delete [] converters;
  61. }
  62. void Perf :: addConverter(PerfConverter *c)
  63. {
  64. if ( nConverters >= maxConverters )
  65. throw std :: runtime_error ( "Too many converters" );
  66. converters[ nConverters++ ] = c;
  67. int prec = c->maxPrecision();
  68. if ( prec > maxPrecision )
  69. maxPrecision = prec;
  70. }
  71. void Perf :: execute (const int count, bool verbose_)
  72. {
  73. verbose = verbose_;
  74. for ( int i = 0; i < count; i++ ) {
  75. double srcDbl = rand ();
  76. srcDbl /= (RAND_MAX + 1.0);
  77. srcDbl *= 20.0;
  78. srcDbl -= 10.0;
  79. float srcFlt = (float) srcDbl;
  80. for ( int prec = 0; prec <= maxPrecision; prec++ ) {
  81. measure (srcFlt, srcDbl, prec);
  82. }
  83. }
  84. report ( "Small numbers, -10..+10", count );
  85. for ( int i = 0; i < count; i++ ) {
  86. double mVal = rand ();
  87. mVal /= (RAND_MAX + 1.0);
  88. double eVal = rand ();
  89. eVal /= (RAND_MAX + 1.0);
  90. double dVal = eVal;
  91. dVal *= FLT_MAX_EXP - FLT_MIN_EXP;
  92. dVal += FLT_MIN_EXP;
  93. int dEVal = static_cast < int > ( dVal + 0.5 );
  94. double srcDbl = ldexp ( mVal, dEVal );
  95. float srcFlt = (float) srcDbl;
  96. for ( int prec = 0; prec <= maxPrecision; prec++ ) {
  97. measure (srcFlt, srcDbl, prec);
  98. }
  99. }
  100. report ( "Random mantissa+exponent", count );
  101. }
  102. void Perf :: report (const char *title, const int count)
  103. {
  104. printf( "\n%s\n\nprec\t", title );
  105. for ( int j = 0; j < nConverters; j++ )
  106. printf( "%-16s ", converters[j]->name() );
  107. for (int prec = 0; prec <= maxPrecision; prec++ ) {
  108. printf( "\n %2d\t", prec );
  109. for (int j = 0; j < nConverters; j++ ) {
  110. PerfConverter *c = converters[j];
  111. if (prec > c->maxPrecision())
  112. printf( "%11s ", "-" );
  113. else {
  114. printf( "%11.9f sec ", c->total(prec) / count );
  115. }
  116. }
  117. }
  118. printf( "\n\n" );
  119. }
  120. void Perf :: measure (double srcD, float srcF, int prec)
  121. {
  122. char buf[40];
  123. for ( int j = 0; j < nConverters; j++ ) {
  124. PerfConverter *c = converters[j];
  125. if (prec > c->maxPrecision())
  126. continue;
  127. std::memset(buf, 0, sizeof(buf));
  128. epicsTime beg = epicsTime :: getMonotonic ();
  129. for ( unsigned i = 0; i < nIterations; i++ ) {
  130. c->target (srcD, srcF, buf, sizeof(buf) - 1, prec);
  131. }
  132. epicsTime end = epicsTime :: getMonotonic ();
  133. double elapsed = end - beg;
  134. elapsed /= nIterations * nUnrolled;
  135. c->add( prec, elapsed );
  136. if (verbose)
  137. printf ( "%17s: %11.9f sec, prec=%2i '%s'\n",
  138. c->name (), elapsed, prec, buf );
  139. }
  140. }
  141. // Conversions to be measured
  142. class PerfCvtFastFloat : public PerfConverter {
  143. static const int digits = 12;
  144. public:
  145. PerfCvtFastFloat ()
  146. {
  147. for (int i = 0; i <= digits; i++)
  148. measured[i] = 0; // Some targets seem to need this
  149. }
  150. int maxPrecision (void) const { return digits; }
  151. const char *name (void) const { return "cvtFloatToString"; }
  152. void target (double srcD, float srcF, char *dst, size_t len, int prec) const
  153. {
  154. cvtFloatToString ( srcF, dst, prec );
  155. cvtFloatToString ( srcF, dst, prec );
  156. cvtFloatToString ( srcF, dst, prec );
  157. cvtFloatToString ( srcF, dst, prec );
  158. cvtFloatToString ( srcF, dst, prec );
  159. cvtFloatToString ( srcF, dst, prec );
  160. cvtFloatToString ( srcF, dst, prec );
  161. cvtFloatToString ( srcF, dst, prec );
  162. cvtFloatToString ( srcF, dst, prec );
  163. cvtFloatToString ( srcF, dst, prec );
  164. }
  165. void add (int prec, double elapsed) { measured[prec] += elapsed; }
  166. double total (int prec) {
  167. double total = measured[prec];
  168. measured[prec] = 0;
  169. return total;
  170. }
  171. private:
  172. double measured[digits+1];
  173. };
  174. class PerfCvtFastDouble : public PerfConverter {
  175. static const int digits = 17;
  176. public:
  177. PerfCvtFastDouble ()
  178. {
  179. for (int i = 0; i <= digits; i++)
  180. measured[i] = 0; // Some targets seem to need this
  181. }
  182. int maxPrecision (void) const { return digits; }
  183. const char *name (void) const { return "cvtDoubleToString"; }
  184. void target (double srcD, float srcF, char *dst, size_t len, int prec) const
  185. {
  186. cvtDoubleToString ( srcD, dst, prec );
  187. cvtDoubleToString ( srcD, dst, prec );
  188. cvtDoubleToString ( srcD, dst, prec );
  189. cvtDoubleToString ( srcD, dst, prec );
  190. cvtDoubleToString ( srcD, dst, prec );
  191. cvtDoubleToString ( srcD, dst, prec );
  192. cvtDoubleToString ( srcD, dst, prec );
  193. cvtDoubleToString ( srcD, dst, prec );
  194. cvtDoubleToString ( srcD, dst, prec );
  195. cvtDoubleToString ( srcD, dst, prec );
  196. }
  197. void add(int prec, double elapsed) { measured[prec] += elapsed; }
  198. double total (int prec) {
  199. double total = measured[prec];
  200. measured[prec] = 0;
  201. return total;
  202. }
  203. private:
  204. double measured[digits+1];
  205. };
  206. class PerfSNPrintf : public PerfConverter {
  207. static const int digits = 17;
  208. public:
  209. PerfSNPrintf ()
  210. {
  211. for (int i = 0; i <= digits; i++)
  212. measured[i] = 0; // Some targets seem to need this
  213. }
  214. int maxPrecision (void) const { return digits; }
  215. const char *name (void) const { return "epicsSnprintf"; }
  216. void target (double srcD, float srcF, char *dst, size_t len, int prec) const
  217. {
  218. epicsSnprintf ( dst, len, "%.*g", prec, srcD );
  219. epicsSnprintf ( dst, len, "%.*g", prec, srcD );
  220. epicsSnprintf ( dst, len, "%.*g", prec, srcD );
  221. epicsSnprintf ( dst, len, "%.*g", prec, srcD );
  222. epicsSnprintf ( dst, len, "%.*g", prec, srcD );
  223. epicsSnprintf ( dst, len, "%.*g", prec, srcD );
  224. epicsSnprintf ( dst, len, "%.*g", prec, srcD );
  225. epicsSnprintf ( dst, len, "%.*g", prec, srcD );
  226. epicsSnprintf ( dst, len, "%.*g", prec, srcD );
  227. epicsSnprintf ( dst, len, "%.*g", prec, srcD );
  228. }
  229. void add(int prec, double elapsed) { measured[prec] += elapsed; }
  230. double total (int prec) {
  231. double total = measured[prec];
  232. measured[prec] = 0;
  233. return total;
  234. }
  235. private:
  236. double measured[digits+1];
  237. };
  238. // This is a quick-and-dirty std::streambuf converter that writes directly
  239. // into the output buffer. Performance is slower than epicsSnprintf().
  240. struct membuf: public std::streambuf {
  241. membuf(char *array, size_t size) {
  242. this->setp(array, array + size - 1);
  243. }
  244. };
  245. struct omemstream: virtual membuf, std::ostream {
  246. omemstream(char *array, size_t size):
  247. membuf(array, size),
  248. std::ostream(this) {
  249. }
  250. };
  251. static void ossConvertD(char *dst, size_t len, int prec, double src) {
  252. omemstream oss(dst, len);
  253. oss.precision(prec);
  254. oss << src << ends;
  255. }
  256. class PerfStreamBuf : public PerfConverter {
  257. static const int digits = 17;
  258. public:
  259. PerfStreamBuf ()
  260. {
  261. for (int i = 0; i <= digits; i++)
  262. measured[i] = 0; // Some targets seem to need this
  263. }
  264. int maxPrecision (void) const { return digits; }
  265. const char *name (void) const { return "std::streambuf"; }
  266. void target (double srcD, float srcF, char *dst, size_t len, int prec) const
  267. {
  268. ossConvertD ( dst, len, prec, srcD );
  269. ossConvertD ( dst, len, prec, srcD );
  270. ossConvertD ( dst, len, prec, srcD );
  271. ossConvertD ( dst, len, prec, srcD );
  272. ossConvertD ( dst, len, prec, srcD );
  273. ossConvertD ( dst, len, prec, srcD );
  274. ossConvertD ( dst, len, prec, srcD );
  275. ossConvertD ( dst, len, prec, srcD );
  276. ossConvertD ( dst, len, prec, srcD );
  277. ossConvertD ( dst, len, prec, srcD );
  278. }
  279. void add(int prec, double elapsed) { measured[prec] += elapsed; }
  280. double total (int prec) {
  281. double total = measured[prec];
  282. measured[prec] = 0;
  283. return total;
  284. }
  285. private:
  286. double measured[digits+1];
  287. };
  288. MAIN(cvtFastPerform)
  289. {
  290. Perf t(4);
  291. t.addConverter( new PerfCvtFastFloat );
  292. t.addConverter( new PerfCvtFastDouble );
  293. t.addConverter( new PerfSNPrintf );
  294. t.addConverter( new PerfStreamBuf );
  295. // The parameter to execute() below are:
  296. // count = number of different random numbers to measure
  297. // verbose = whether to display individual measurements
  298. #ifdef vxWorks
  299. t.execute (3, true); // Slow...
  300. #else
  301. t.execute (5, false);
  302. #endif
  303. return 0;
  304. }