1 // -*- C++ -*-
2 //  Boost general library 'format'  ---------------------------
3 //  See http://www.boost.org for updates, documentation, and revision history.
4 
5 //  Copyright (c) 2001 Samuel Krempp
6 //                  krempp@crans.ens-cachan.fr
7 //  Distributed under the Boost Software License, Version 1.0. (See
8 //  accompanying file LICENSE_1_0.txt or copy at
9 //  http://www.boost.org/LICENSE_1_0.txt)
10 
11 // several suggestions from Jens Maurer
12 
13 // ------------------------------------------------------------------------------
14 // bench_variants.cc :  do the same task, with sprintf, stream, and format
15 //                      and compare their times.
16 
17 // This benchmark is provided purely for information.
18 // It might not even compile as-is,
19 //   or not give any sensible results.
20 //      (e.g., it expects sprintf to be POSIX compliant)
21 
22 // ------------------------------------------------------------------------------
23 
24 
25 #include <iostream>
26 #include <iomanip>
27 #include <cstdio>  // sprintf
28 #include <cstring>
29 #include <fstream>
30 #include <cmath>   // floor
31 #include <boost/timer.hpp>
32 #include <vector>
33 
34 #include <boost/format.hpp>
35 
36 // portable /dev/null stream equivalent, by James Kanze, http://www.gabi-soft.de
37 class NulStreambuf : public std::streambuf
38 {
39 public:
NulStreambuf()40   NulStreambuf() {
41       setp( dummyBuffer , dummyBuffer + 64 ) ;
42   }
43   virtual int  overflow( int c );
44   virtual int  underflow();
45 private:
46     char                dummyBuffer[ 64 ] ;
47 } ;
48 
49 class NulStream : public std::basic_ostream<char, std::char_traits<char> >
50 {
51 public:
52   NulStream();
53   virtual ~NulStream();
rdbuf()54   NulStreambuf*    rdbuf() {
55     return static_cast< NulStreambuf* >(
56                    ((std::basic_ostream<char, std::char_traits<char> > *) this) -> rdbuf() ) ;
57   }
58 } ;
59 
60 
61 //-------------------------------------------------------------------------------------
62 //   NulStream implementation
63 
NulStream()64 NulStream::NulStream()  : std::basic_ostream<char, std::char_traits<char> > (NULL) {
65   init( new NulStreambuf ) ;
66 }
67 
~NulStream()68 NulStream::~NulStream() {
69     delete rdbuf() ;
70 }
71 
underflow()72 int  NulStreambuf::underflow(){ return std::ios::traits_type::eof();
73 }
74 
overflow(int c)75 int NulStreambuf::overflow( int c ){
76     setp( dummyBuffer , dummyBuffer + 64 ) ;
77     return (c == std::ios::traits_type::eof()) ? '\0' : c ;
78 }
79 
80 
81 
82 // -------------------------------------------------------------------------------------
83 
84 namespace benchmark {
85 
86 static int NTests = 300000;
87 
88 //static std::stringstream nullStream;
89 static NulStream nullStream;
90 static double tstream, tpf;
91 //static const std::string fstring="%3$#x %1$20.10E %2$g %3$d \n";
92 static const std::string fstring="%3$0#6x %1$20.10E %2$g %3$0+5d \n";
93 static const double     arg1=45.23;
94 static const double     arg2=12.34;
95 static const int        arg3=23;
96 static const std::string res =
97 "0x0017     4.5230000000E+01 12.34 +0023 \n";
98 //static const std::string res = "23.0000     4.5230000000E+01 12.34 23 \n";
99 
100 void test_sprintf();
101 void test_nullstream();
102 void test_opti_nullstream();
103 void test_parsed_once_format();
104 void test_reused_format();
105 void test_format();
106 void test_try1();
107 void test_try2();
108 
test_sprintf()109 void test_sprintf()
110 {
111     using namespace std;
112 
113     vector<char> bufr;
114     bufr.reserve(4000);
115     char *buf = &bufr[0];
116 
117     // Check that sprintf is Unix98 compatible on the platform :
118     sprintf(buf, fstring.c_str(), arg1, arg2, arg3);
119     if( strncmp( buf, res.c_str(), res.size()) != 0 ) {
120       cerr << endl << buf;
121     }
122     // time the loop :
123     boost::timer chrono;
124     for(int i=0; i<NTests; ++i) {
125       sprintf(buf, fstring.c_str(), arg1, arg2, arg3);
126     }
127     tpf=chrono.elapsed();
128     cout  << left << setw(20) <<"printf time"<< right <<":" << tpf  << endl;
129 }
130 
test_try1()131 void test_try1()
132 {
133   using namespace std;
134   boost::io::basic_oaltstringstream<char> oss;
135   oss << boost::format(fstring) % arg1 % arg2 % arg3;
136   boost::timer chrono;
137   size_t dummy=0;
138   for(int i=0; i<NTests; ++i) {
139       dummy += oss.cur_size();
140   }
141   double t = chrono.elapsed();
142   cout  << left << setw(20) <<"try1 time"<< right <<":" << setw(5) << t
143         << ",  = " << t / tpf << " * printf "
144         << ",  = " << t / tstream << " * nullStream \n";
145 }
146 
test_try2()147 void test_try2()
148 {
149   using namespace std;
150   boost::io::basic_oaltstringstream<char> oss;
151   oss << boost::format(fstring) % arg1 % arg2 % arg3;
152   oss << "blas 34567890GGGGGGGGGGGGGGGGGGGGGGGGGGGGggggggggggggggggggggggggggg " << endl;
153   string s = oss.cur_str();
154   oss << s << s << s;
155   oss.clear_buffer();
156   oss << s << s;
157   s = oss.cur_str();
158   boost::timer chrono;
159   size_t dummy=0;
160   for(int i=0; i<NTests; ++i) {
161       dummy += oss.cur_size();
162   }
163   double t = chrono.elapsed();
164   cout  << left << setw(20) <<"try2 time"<< right <<":" << setw(5) << t
165         << ",  = " << t / tpf << " * printf "
166         << ",  = " << t / tstream << " * nullStream \n";
167 }
168 
do_stream(std::ostream & os)169 void do_stream(std::ostream& os) {
170     using namespace std;
171     std::ios_base::fmtflags f = os.flags();
172     os << hex << showbase << internal << setfill('0') << setw(6) << arg3
173        << dec << noshowbase << right << setfill(' ')
174        << " "
175        << scientific << setw(20) << setprecision(10) << uppercase << arg1
176        << setprecision(6) << nouppercase ;
177     os.flags(f);
178     os << " " << arg2 << " "
179        << showpos << setw(5) << internal << setfill('0') << arg3 << " \n" ;
180     os.flags(f);
181 }
182 
test_nullstream()183 void test_nullstream()
184 {
185     using namespace std;
186     boost::timer chrono;
187     boost::io::basic_oaltstringstream<char> oss;
188 
189     {
190         do_stream(oss);
191         if(oss.str() != res ) {
192             cerr << endl << oss.str() ;
193         }
194     }
195 
196     for(int i=0; i<NTests; ++i) {
197         do_stream(nullStream);
198     }
199 
200 //     for(int i=0; i<NTests; ++i) {
201 //       std::ios_base::fmtflags f0 = nullStream.flags();
202 //       nullStream << hex << showbase << arg3
203 //                  << dec << noshowbase << " "
204 //                  << scientific << setw(20) << setprecision(10) << uppercase <<  arg1
205 //                  << setprecision(0);
206 //       nullStream.flags(f0);
207 //       nullStream << " " << arg2 << " " << arg3 << " \n" ;
208 
209 //     }
210     double t = chrono.elapsed();
211     cout  << left << setw(20) <<"ostream time"<< right <<":" << setw(5) << t
212           << ",  = " << t / tpf << " * printf \n";
213     tstream = t;
214 }
215 
test_opti_nullstream()216 void test_opti_nullstream()
217 {
218     using namespace std;
219     boost::timer chrono;
220     boost::io::basic_oaltstringstream<char> oss;
221     //static const std::string fstring="%3$#x %1$20.10E %2$g %3$d \n";
222 
223     std::ios_base::fmtflags f0 = oss.flags(), f1, f2;
224     streamsize p0 = oss.precision();
225     {
226       oss << hex << showbase;
227       f1 = oss.flags();
228       oss << arg3;
229 
230       oss.flags(f0);
231       oss << " " << scientific << setw(20) << setprecision(10) << uppercase;
232       f2 = oss.flags();
233       oss << arg1;
234 
235       oss.flags(f0); oss.precision(p0);
236       oss << " " << arg2 << " " << arg3 << " \n" ;
237 
238       if(oss.str() != res ) {
239         cerr << endl << oss.str() ;
240       }
241     }
242 
243     for(int i=0; i<NTests; ++i) {
244       nullStream.flags(f1);
245       nullStream << arg3;
246 
247       nullStream << setw(20) << setprecision(10);
248       nullStream.flags(f2);
249       nullStream << arg1;
250 
251       nullStream.flags(f0); nullStream.precision(p0);
252       nullStream << " " << arg2 << " " << arg3 << " \n" ;
253     }
254     double t = chrono.elapsed();
255     cout  << left << setw(20) <<"opti-stream time"<< right <<":" << setw(5) << t
256           << ",  = " << t / tpf << " * printf \n";
257     //    tstream = t;
258 }
259 
test_parsed_once_format()260 void test_parsed_once_format()
261 {
262     using namespace std;
263     static const boost::format fmter(fstring);
264 
265     boost::io::basic_oaltstringstream<char> oss;
266     oss << boost::format(fmter) % arg1 % arg2 % arg3 ;
267     if( oss.str() != res ) {
268       cerr << endl << oss.str();
269     }
270 
271     // not only is the format-string parsed once,
272     // but also the buffer of the internal stringstream is already allocated.
273 
274     boost::timer chrono;
275     for(int i=0; i<NTests; ++i) {
276         nullStream << boost::format(fmter) % arg1 % arg2 % arg3;
277     }
278     double t=chrono.elapsed();
279     cout  << left << setw(20) <<"parsed-once time"<< right <<":" << setw(5) << t
280           << ",  = " << t / tpf << " * printf "
281           << ",  = " << t / tstream << " * nullStream \n";
282 }
283 
test_reused_format()284 void test_reused_format()
285 {
286   using namespace std;
287   boost::io::basic_oaltstringstream<char> oss;
288   oss << boost::format(fstring) % arg1 % arg2 % arg3;
289   if(oss.str() != res ) {
290     cerr << endl << oss.str();
291   }
292 
293   boost::timer chrono;
294   boost::format fmter;
295   for(int i=0; i<NTests; ++i) {
296     nullStream << fmter.parse(fstring) % arg1 % arg2 % arg3;
297   }
298   double t = chrono.elapsed();
299   cout  << left << setw(20) <<"reused format time"<< right <<":" << setw(5) << t
300         << ",  = " << t / tpf << " * printf "
301         << ",  = " << t / tstream << " * nullStream \n";
302 }
303 
test_format()304 void test_format()
305 {
306   using namespace std;
307   boost::io::basic_oaltstringstream<char> oss;
308   oss << boost::format(fstring) % arg1 % arg2 % arg3;
309   if(oss.str() != res ) {
310     cerr << endl << oss.str();
311   }
312 
313   boost::timer chrono;
314   for(int i=0; i<NTests; ++i) {
315     nullStream << boost::format(fstring) % arg1 % arg2 % arg3;
316   }
317   double t = chrono.elapsed();
318   cout  << left << setw(20) <<"format time"<< right <<":" << setw(5) << t
319         << ",  = " << t / tpf << " * printf "
320         << ",  = " << t / tstream << " * nullStream \n";
321 }
322 
323 } // benchmark
324 
main(int argc,char * argv[])325 int main(int argc, char * argv[]) {
326     using namespace benchmark;
327     using namespace boost;
328     using namespace std;
329     const string::size_type  npos = string::npos;
330 
331     string choices = "";
332     if (1<argc) {
333         choices = (argv[1]); // profiling is easier launching only one.
334         NTests = 1000 * 1000;  // andmoreprecise with many iterations
335         cout << "choices (" << choices << ") \n";
336     }
337 
338     if (choices == "" || choices.find('p') != npos)
339         test_sprintf();
340     if (choices == "" || choices.find('n') != npos)
341         test_nullstream();
342     if (choices == "" || choices.find('1') != npos)
343         test_parsed_once_format();
344     if (choices == "" || choices.find('r') != npos)
345         test_reused_format();
346     if (choices == "" || choices.find('f') != npos)
347         test_format();
348     if (choices.find('t') != npos)
349         test_try1();
350     if (choices.find('y') != npos)
351         test_try2();
352     if (choices.find('o') != npos)
353         test_opti_nullstream();
354     return 0;
355 }
356 
357