Add benchmarks for various ways to compare strings.
[wxWidgets.git] / tests / benchmarks / strings.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: tests/benchmarks/strings.cpp
3 // Purpose: String-related benchmarks
4 // Author: Vadim Zeitlin
5 // Created: 2008-07-19
6 // RCS-ID: $Id$
7 // Copyright: (c) 2008 Vadim Zeitlin <vadim@wxwidgets.org>
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 #include "wx/string.h"
12 #include "wx/ffile.h"
13
14 #include "bench.h"
15 #include "htmlparser/htmlpars.h"
16
17 static const char asciistr[] =
18 "This is just the first line of a very long 7 bit ASCII string"
19 "This is just the second line of a very long 7 bit ASCII string"
20 "This is just the third line of a very long 7 bit ASCII string"
21 "This is just the fourth line of a very long 7 bit ASCII string"
22 "This is just the fifth line of a very long 7 bit ASCII string"
23 "This is just the sixth line of a very long 7 bit ASCII string"
24 "This is just the seventh line of a very long 7 bit ASCII string"
25 "This is just the eighth line of a very long 7 bit ASCII string"
26 "This is just the ninth line of a very long 7 bit ASCII string"
27 "This is just the tenth line of a very long 7 bit ASCII string"
28 ;
29
30 static const char utf8str[] =
31 "\xD0\xA6\xD0\xB5\xD0\xBB\xD0\xBE\xD0\xB5 \xD1\x87\xD0\xB8\xD1\x81\xD0\xBB\xD0\xBE 0"
32 "\xD0\xA6\xD0\xB5\xD0\xBB\xD0\xBE\xD0\xB5 \xD1\x87\xD0\xB8\xD1\x81\xD0\xBB\xD0\xBE 1"
33 "\xD0\xA6\xD0\xB5\xD0\xBB\xD0\xBE\xD0\xB5 \xD1\x87\xD0\xB8\xD1\x81\xD0\xBB\xD0\xBE 2"
34 "\xD0\xA6\xD0\xB5\xD0\xBB\xD0\xBE\xD0\xB5 \xD1\x87\xD0\xB8\xD1\x81\xD0\xBB\xD0\xBE 3"
35 "\xD0\xA6\xD0\xB5\xD0\xBB\xD0\xBE\xD0\xB5 \xD1\x87\xD0\xB8\xD1\x81\xD0\xBB\xD0\xBE 4"
36 "\xD0\xA6\xD0\xB5\xD0\xBB\xD0\xBE\xD0\xB5 \xD1\x87\xD0\xB8\xD1\x81\xD0\xBB\xD0\xBE 5"
37 "\xD0\xA6\xD0\xB5\xD0\xBB\xD0\xBE\xD0\xB5 \xD1\x87\xD0\xB8\xD1\x81\xD0\xBB\xD0\xBE 6"
38 "\xD0\xA6\xD0\xB5\xD0\xBB\xD0\xBE\xD0\xB5 \xD1\x87\xD0\xB8\xD1\x81\xD0\xBB\xD0\xBE 7"
39 "\xD0\xA6\xD0\xB5\xD0\xBB\xD0\xBE\xD0\xB5 \xD1\x87\xD0\xB8\xD1\x81\xD0\xBB\xD0\xBE 8"
40 "\xD0\xA6\xD0\xB5\xD0\xBB\xD0\xBE\xD0\xB5 \xD1\x87\xD0\xB8\xD1\x81\xD0\xBB\xD0\xBE 9"
41 ;
42
43 namespace
44 {
45
46 const wxString& GetTestAsciiString()
47 {
48 static wxString testString;
49 if ( testString.empty() )
50 {
51 long num = Bench::GetNumericParameter();
52 if ( !num )
53 num = 1;
54
55 for ( long n = 0; n < num; n++ )
56 testString += wxString::FromAscii(asciistr);
57 }
58
59 return testString;
60 }
61
62 } // anonymous namespace
63
64 // this is just a baseline
65 BENCHMARK_FUNC(Strlen)
66 {
67 if ( strlen(utf8str) != WXSIZEOF(utf8str) - 1 )
68 return false;
69
70 if ( strlen(asciistr) != WXSIZEOF(asciistr) - 1 )
71 return false;
72
73 return true;
74 }
75
76 // ----------------------------------------------------------------------------
77 // FromUTF8() benchmarks
78 // ----------------------------------------------------------------------------
79
80 BENCHMARK_FUNC(FromUTF8)
81 {
82 wxString s = wxString::FromUTF8(utf8str);
83 if ( s.empty() )
84 return false;
85
86 s = wxString::FromUTF8(asciistr);
87 if ( s.empty() )
88 return false;
89
90 return true;
91 }
92
93 BENCHMARK_FUNC(FromUTF8WithNpos)
94 {
95 wxString s = wxString::FromUTF8(utf8str, wxString::npos);
96 if ( s.empty() )
97 return false;
98
99 s = wxString::FromUTF8(asciistr, wxString::npos);
100 if ( s.empty() )
101 return false;
102
103 return true;
104 }
105
106 BENCHMARK_FUNC(FromUTF8WithLen)
107 {
108 wxString s = wxString::FromUTF8(utf8str, WXSIZEOF(utf8str));
109 if ( s.empty() )
110 return false;
111
112 s = wxString::FromUTF8(asciistr, WXSIZEOF(asciistr));
113 if ( s.empty() )
114 return false;
115
116 return true;
117 }
118
119 // ----------------------------------------------------------------------------
120 // FromUTF8Unchecked() benchmarks
121 // ----------------------------------------------------------------------------
122
123 BENCHMARK_FUNC(FromUTF8Unchecked)
124 {
125 wxString s = wxString::FromUTF8Unchecked(utf8str);
126 if ( s.empty() )
127 return false;
128
129 s = wxString::FromUTF8Unchecked(asciistr);
130 if ( s.empty() )
131 return false;
132
133 return true;
134 }
135
136 BENCHMARK_FUNC(FromUTF8UncheckedWithNpos)
137 {
138 wxString s = wxString::FromUTF8Unchecked(utf8str, wxString::npos);
139 if ( s.empty() )
140 return false;
141
142 s = wxString::FromUTF8Unchecked(asciistr, wxString::npos);
143 if ( s.empty() )
144 return false;
145
146 return true;
147 }
148
149 BENCHMARK_FUNC(FromUTF8UncheckedWithLen)
150 {
151 wxString s = wxString::FromUTF8Unchecked(utf8str, WXSIZEOF(utf8str));
152 if ( s.empty() )
153 return false;
154
155 s = wxString::FromUTF8Unchecked(asciistr, WXSIZEOF(asciistr));
156 if ( s.empty() )
157 return false;
158
159 return true;
160 }
161
162 // ----------------------------------------------------------------------------
163 // FromAscii() benchmarks
164 // ----------------------------------------------------------------------------
165
166 BENCHMARK_FUNC(FromAscii)
167 {
168 wxString s = wxString::FromAscii(asciistr);
169 if ( s.empty() )
170 return false;
171
172 return true;
173 }
174
175 BENCHMARK_FUNC(FromAsciiWithNpos)
176 {
177 wxString s = wxString::FromAscii(asciistr);
178 if ( s.empty() )
179 return false;
180
181 return true;
182 }
183
184 BENCHMARK_FUNC(FromAsciiWithLen)
185 {
186 wxString s = wxString::FromAscii(asciistr, WXSIZEOF(asciistr));
187 if ( s.empty() )
188 return false;
189
190 return true;
191 }
192
193 // ----------------------------------------------------------------------------
194 // simple string iteration
195 // ----------------------------------------------------------------------------
196
197 // baseline
198 BENCHMARK_FUNC(ForCString)
199 {
200 for ( size_t n = 0; n < WXSIZEOF(asciistr); n++ )
201 {
202 if ( asciistr[n] == '~' )
203 return false;
204 }
205
206 return true;
207 }
208
209 BENCHMARK_FUNC(ForStringIndex)
210 {
211 const wxString& s = GetTestAsciiString();
212 const size_t len = s.length();
213 for ( size_t n = 0; n < len; n++ )
214 {
215 if ( s[n] == '~' )
216 return false;
217 }
218
219 return true;
220 }
221
222 BENCHMARK_FUNC(ForStringIter)
223 {
224 const wxString& s = GetTestAsciiString();
225 const wxString::const_iterator end = s.end();
226 for ( wxString::const_iterator i = s.begin(); i != end; ++i )
227 {
228 if ( *i == '~' )
229 return false;
230 }
231
232 return true;
233 }
234
235 BENCHMARK_FUNC(ForStringRIter)
236 {
237 const wxString& s = GetTestAsciiString();
238 const wxString::const_reverse_iterator rend = s.rend();
239 for ( wxString::const_reverse_iterator i = s.rbegin(); i != rend; ++i )
240 {
241 if ( *i == '~' )
242 return false;
243 }
244
245 return true;
246 }
247
248 // ----------------------------------------------------------------------------
249 // wxString::Replace()
250 // ----------------------------------------------------------------------------
251
252 const size_t ASCIISTR_LEN = strlen(asciistr);
253
254 BENCHMARK_FUNC(ReplaceLoop)
255 {
256 wxString str('x', ASCIISTR_LEN);
257 for ( size_t n = 0; n < ASCIISTR_LEN; n++ )
258 {
259 if ( str[n] == 'a' )
260 str[n] = 'z';
261 }
262
263 return str.length() != 0;
264 }
265
266 BENCHMARK_FUNC(ReplaceNone)
267 {
268 wxString str('x', ASCIISTR_LEN);
269 return str.Replace("a", "z") == 0;
270 }
271
272 BENCHMARK_FUNC(ReplaceSome)
273 {
274 wxString str(asciistr);
275 return str.Replace("7", "8") != 0;
276 }
277
278 BENCHMARK_FUNC(ReplaceAll)
279 {
280 wxString str('x', ASCIISTR_LEN);
281 return str.Replace("x", "y") != 0;
282 }
283
284 BENCHMARK_FUNC(ReplaceLonger)
285 {
286 wxString str('x', ASCIISTR_LEN);
287 return str.Replace("x", "yy") != 0;
288 }
289
290 BENCHMARK_FUNC(ReplaceShorter)
291 {
292 wxString str('x', ASCIISTR_LEN);
293 return str.Replace("xx", "y") != 0;
294 }
295
296 // ----------------------------------------------------------------------------
297 // string case conversion
298 // ----------------------------------------------------------------------------
299
300 BENCHMARK_FUNC(Lower)
301 {
302 return GetTestAsciiString().Lower().length() > 0;
303 }
304
305 BENCHMARK_FUNC(Upper)
306 {
307 return GetTestAsciiString().Upper().length() > 0;
308 }
309
310 // ----------------------------------------------------------------------------
311 // string comparison
312 // ----------------------------------------------------------------------------
313
314 BENCHMARK_FUNC(StrcmpA)
315 {
316 const wxString& s = GetTestAsciiString();
317
318 return wxCRT_StrcmpA(s, s) == 0;
319 }
320
321 BENCHMARK_FUNC(StrcmpW)
322 {
323 const wxString& s = GetTestAsciiString();
324
325 return wxCRT_StrcmpW(s, s) == 0;
326 }
327
328 BENCHMARK_FUNC(StricmpA)
329 {
330 const wxString& s = GetTestAsciiString();
331
332 return wxCRT_StricmpA(s, s) == 0;
333 }
334
335 BENCHMARK_FUNC(StricmpW)
336 {
337 const wxString& s = GetTestAsciiString();
338
339 return wxCRT_StricmpW(s, s) == 0;
340 }
341
342 BENCHMARK_FUNC(StringCmp)
343 {
344 const wxString& s = GetTestAsciiString();
345
346 return s.Cmp(s) == 0;
347 }
348
349 BENCHMARK_FUNC(StringCmpNoCase)
350 {
351 const wxString& s = GetTestAsciiString();
352
353 return s.CmpNoCase(s) == 0;
354 }
355
356 // Also benchmark various native functions under MSW. Surprisingly/annoyingly
357 // they sometimes have vastly better performance than alternatives, especially
358 // for case-sensitive comparison (see #10375).
359 #ifdef __WXMSW__
360
361 #include "wx/msw/wrapwin.h"
362
363 BENCHMARK_FUNC(MSWlstrcmp)
364 {
365 const wxString& s = GetTestAsciiString();
366
367 return lstrcmp(s.t_str(), s.t_str()) == 0;
368 }
369
370 BENCHMARK_FUNC(MSWlstrcmpi)
371 {
372 const wxString& s = GetTestAsciiString();
373
374 return lstrcmpi(s.t_str(), s.t_str()) == 0;
375 }
376
377 BENCHMARK_FUNC(MSWCompareString)
378 {
379 const wxString& s = GetTestAsciiString();
380
381 return ::CompareString
382 (
383 LOCALE_USER_DEFAULT,
384 0,
385 s.t_str(), s.length(),
386 s.t_str(), s.length()
387 ) == CSTR_EQUAL;
388 }
389
390 BENCHMARK_FUNC(MSWCompareStringIgnoreCase)
391 {
392 const wxString& s = GetTestAsciiString();
393
394 return ::CompareString
395 (
396 LOCALE_USER_DEFAULT,
397 NORM_IGNORECASE,
398 s.t_str(), s.length(),
399 s.t_str(), s.length()
400 ) == CSTR_EQUAL;
401 }
402
403 #endif // __WXMSW__
404
405 // ----------------------------------------------------------------------------
406 // string buffers: wx[W]CharBuffer
407 // ----------------------------------------------------------------------------
408
409 BENCHMARK_FUNC(CharBuffer)
410 {
411 wxString str(asciistr);
412
413 // NB: wxStrlen() is here to simulate some use of the returned buffer.
414 // Both mb_str() and wc_str() are used so that this code does something
415 // nontrivial in any build.
416 return wxStrlen(str.mb_str()) == ASCIISTR_LEN &&
417 wxStrlen(str.wc_str()) == ASCIISTR_LEN;
418 }
419
420
421 // ----------------------------------------------------------------------------
422 // wxString::operator[] - parse large HTML page
423 // ----------------------------------------------------------------------------
424
425 class DummyParser : public wx28HtmlParser
426 {
427 public:
428 virtual wxObject* GetProduct() { return NULL; }
429 virtual void AddText(const wxChar*) {}
430 };
431
432
433 BENCHMARK_FUNC(ParseHTML)
434 {
435 // static so that construction time is not counted
436 static DummyParser parser;
437 static wxString html;
438 if ( html.empty() )
439 {
440 wxString html1;
441 wxFFile("htmltest.html").ReadAll(&html1, wxConvUTF8);
442
443 // this is going to make for some invalid HTML, of course, but it
444 // doesn't really matter
445 long num = Bench::GetNumericParameter();
446 if ( !num )
447 num = 1;
448
449 for ( long n = 0; n < num; n++ )
450 html += html1;
451 }
452
453 parser.Parse(html);
454
455 return true;
456 }