wx printf() implementation bug fixes ('%' handling, thread safety, ...) and optimisat...
[wxWidgets.git] / tests / benchmarks / printfbench.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: printfbench.cpp
3 // Purpose: A sample console app which benchmarks wxPrintf*() functions
4 // Author: Francesco Montorsi
5 // Modified by:
6 // Created: 27/3/2006
7 // RCS-ID: $Id$
8 // Copyright: (c) 2006 Francesco Montorsi
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #include <wx/string.h>
21 #include <wx/stopwatch.h>
22 #include <wx/utils.h>
23 #include <wx/cmdline.h>
24 #include <wx/app.h>
25
26
27 // ----------------------------------------------------------------------------
28 // command line
29 // ----------------------------------------------------------------------------
30
31 #define HELP_SWITCH wxT("h")
32 #define NUMBENCHMARK_OPTION wxT("n")
33
34 static const wxCmdLineEntryDesc g_cmdLineDesc[] =
35 {
36 { wxCMD_LINE_SWITCH, HELP_SWITCH, wxT("help"),
37 wxT("displays help on the command line parameters") },
38
39 { wxCMD_LINE_OPTION, NUMBENCHMARK_OPTION, wxT("numtest"),
40 wxT("the number of wxPrintf() calls to benchmark"), wxCMD_LINE_VAL_NUMBER },
41
42 { wxCMD_LINE_NONE }
43 };
44
45
46 // ----------------------------------------------------------------------------
47 // constants
48 // ----------------------------------------------------------------------------
49
50 #define DEFAULT_NUMBENCHMARKS 100000
51 #define BUFSIZE 10000
52
53 // set wxTEST_WX_ONLY to 1 when you want to profile wx's implementation only.
54 // A little reminder about profiling under Linux:
55 //
56 // 1) configure wxWidgets in release mode
57 // 2) make sure that HAVE_UNIX98_PRINTF is undefined (just #defining it to zero
58 // does not work; you must comment out the entire #define) in your setup.h;
59 // and also that wxUSE_PRINTF_POS_PARAMS is set to 1; this will force the
60 // use of wx's own implementation of wxVsnprintf()
61 // 3) compile wx
62 // 4) set wxTEST_WX_ONLY to 1 and compile tests as well
63 // 5) run "callgrind ./printfbench"
64 // 6) run "kcachegrind dump_file_generated_by_callgrind"
65 //
66 #define wxTEST_WX_ONLY 1
67
68
69 const wxString g_verylongString =
70 wxT("very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very ")
71 wxT("very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long string!\n\n\n");
72
73
74 // ----------------------------------------------------------------------------
75 // benchmarking helpers
76 // ----------------------------------------------------------------------------
77
78 #define DO_LONG_BENCHMARK(fnc) \
79 fnc(buffer, BUFSIZE, \
80 wxT("This is a reasonably long string with various %s arguments, exactly %d, ") \
81 wxT("and is used as benchmark for %s - %% %.2f %d %s"), \
82 wxT("(many!!)"), 6, wxT("this program"), 23.342f, 999, \
83 g_verylongString.c_str());
84
85 #define DO_LONG_POSITIONAL_BENCHMARK(fnc) \
86 fnc(buffer, BUFSIZE, \
87 wxT("This is a %2$s and thus is harder to parse... let's %1$s ") \
88 wxT("for our benchmarking aims - %% %3$f %5$d %4$s"), \
89 wxT("test it"), wxT("string with positional arguments"), 23.342f, \
90 g_verylongString.c_str(), 999);
91
92 #define DO_BENCHMARK(fnc) \
93 fnc(buffer, BUFSIZE, \
94 wxT("This is a short %s string with very few words"), wxT("test"));
95
96 #define DO_POSITIONAL_BENCHMARK(fnc) \
97 fnc(buffer, BUFSIZE, \
98 wxT("This is a %2$s and thus is harder to parse... nonetheless, %1$s !"), \
99 wxT("test it"), wxT("string with positional arguments"));
100
101 // the configure script of wxWidgets will define HAVE_UNIX98_PRINTF on those
102 // system with a *printf() family of functions conformant to Unix 98 standard;
103 // systems without the configure script as build system (e.g. Windows) do not
104 // have positional support anyway
105 #ifdef HAVE_UNIX98_PRINTF
106 #define wxSYSTEM_HAS_POSPARAM_SUPPORT 1
107 #else
108 #define wxSYSTEM_HAS_POSPARAM_SUPPORT 1
109 #endif
110
111 // we need to avoid the use of wxPrintf() here since it could have been mapped
112 // to wxWidgets' implementation of wxVsnPrintf() !
113 #if wxUSE_UNICODE
114 #define sys_printf swprintf
115 #else
116 #define sys_printf snprintf
117 #endif
118
119 // the given stopwatch returns a time delta in milliseconds thus this macro
120 // returns the number of microseconds required for a single *printf() call
121 #define wxFMT(sw) ((double)((sw.Time()*1000.0)/tests))
122
123
124
125
126 // ----------------------------------------------------------------------------
127 // main
128 // ----------------------------------------------------------------------------
129
130 int main(int argc, char **argv)
131 {
132 wxApp::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "program");
133 wxInitializer initializer;
134 if ( !initializer )
135 {
136 fprintf(stderr, "Failed to initialize the wxWidgets library, aborting.");
137
138 return -1;
139 }
140
141 // parse the command line
142 // ----------------------
143
144 wxCmdLineParser cmdParser(g_cmdLineDesc, argc, argv);
145 if (cmdParser.Parse() != 0)
146 return false;
147
148 if (cmdParser.Found(HELP_SWITCH))
149 {
150 cmdParser.Usage();
151 return false;
152 }
153
154 long tests;
155 if (!cmdParser.Found(NUMBENCHMARK_OPTION, &tests))
156 tests = DEFAULT_NUMBENCHMARKS;
157
158
159
160 // print some info useful to compare different benchmarks
161 // -------------------------------------------------------
162
163 wxPrintf(wxT("\nRunning on %s\n"), wxGetOsDescription().c_str());
164 wxPrintf(wxT("Compiled in %s-%s-%s mode...\n\n"),
165 #if wxUSE_UNICODE
166 wxT("unicode"),
167 #else
168 wxT("ansi"),
169 #endif
170 #ifdef __WXDEBUG__
171 wxT("debug"),
172 #else
173 wxT("release"),
174 #endif
175 #ifdef WXUSINGDLL
176 wxT("shared")
177 #else
178 wxT("static")
179 #endif
180 );
181 wxPrintf(wxT("Running %ld tests, for each configuration/implementation\n\n"), tests);
182
183
184
185 // start!
186 // ----------------------
187
188 wxChar buffer[BUFSIZE];
189
190 #if !wxTEST_WX_ONLY
191
192 #if wxUSE_PRINTF_POS_PARAMS
193 wxStopWatch wxPos;
194 for (int i=0; i < tests; i++)
195 {
196 DO_LONG_POSITIONAL_BENCHMARK(wxSnprintf)
197 DO_POSITIONAL_BENCHMARK(wxSnprintf)
198 }
199 wxPos.Pause();
200 #endif
201
202 // benchmark system implementation of snprintf()
203 wxStopWatch sys;
204 for (int i=0; i < tests; i++)
205 {
206 DO_LONG_BENCHMARK(sys_printf)
207 DO_BENCHMARK(sys_printf)
208 }
209 sys.Pause();
210
211 #if wxSYSTEM_HAS_POSPARAM_SUPPORT
212 wxStopWatch sysPos;
213 for (int i=0; i < tests; i++)
214 {
215 DO_LONG_POSITIONAL_BENCHMARK(wxSnprintf)
216 DO_POSITIONAL_BENCHMARK(wxSnprintf)
217 }
218 sysPos.Pause();
219 #endif
220
221 #else // !wxTEST_WX_ONLY
222
223 // fake stopwatches
224 wxStopWatch wxPos, sys, sysPos;
225 wxPos.Pause();
226 sys.Pause();
227 sysPos.Pause();
228
229 #endif // !wxTEST_WX_ONLY
230
231 // benchmark wxWidgets implementation of wxSnprintf()
232 wxStopWatch wx;
233 for (int i=0; i < tests; i++)
234 {
235 DO_LONG_BENCHMARK(wxSnprintf)
236 DO_BENCHMARK(wxSnprintf)
237 }
238 wx.Pause();
239
240 // print results
241 // ----------------------
242
243 wxPrintf(wxT("\n ============================== RESULTS ==============================\n"));
244 wxPrintf(wxT(" => Time for the system's snprintf(): %.5f microsec\n"), wxFMT(sys));
245 #if wxSYSTEM_HAS_POSPARAM_SUPPORT
246 wxPrintf(wxT(" => Time for the system's snprintf() with positionals: %.5f microsec\n"), wxFMT(sysPos));
247 #endif
248 wxPrintf(wxT(" => Time for wxSnprintf(): %.5f microsec\n"), wxFMT(wx));
249 #if wxUSE_PRINTF_POS_PARAMS
250 wxPrintf(wxT(" => Time for wxSnprintf() with positionals: %.5f microsec\n"), wxFMT(wxPos));
251 #endif
252
253 double medium;
254 #if wxSYSTEM_HAS_POSPARAM_SUPPORT && wxUSE_PRINTF_POS_PARAMS
255 medium = ((double)wx.Time() / (double)sys.Time() + (double)wxPos.Time() / (double)sysPos.Time()) / 2;
256 #else
257 medium = (double)wx.Time() / (double)sys.Time();
258 #endif
259
260 if (medium > 0.0)
261 {
262 // sometimes it happens that with a small number of tests, medium results zero;
263 // in that case doing the 1.0/medium will not be a wise thing!
264 wxPrintf(wxT("\nwxWidgets implementation is %.3f times slower\n")
265 wxT("(i.e. %.3f times faster) than system implementation.\n"), medium, 1.0/medium);
266 }
267
268 wxPrintf(wxT("\n\n"));
269 return 0;
270 }
271