{"id":349,"date":"2008-09-18T20:55:13","date_gmt":"2008-09-18T08:55:13","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=349"},"modified":"2009-08-07T14:59:07","modified_gmt":"2009-08-07T02:59:07","slug":"delphi-2009-string-performance","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/349\/","title":{"rendered":"Delphi 2009 &#8211; String Performance"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">6<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p><em><strong><span style=\"color: #339966;\">NOTE: Downloads are now fixed!<\/span><\/strong><\/em><\/p>\n<p>Andreas Hausladen generously took the time to make some detailed comments on <a href=\"https:\/\/www.deltics.co.nz\/blog\/?p=338\" target=\"_blank\">my previous post<\/a>, one of which prompted me to throw together some further performance test cases for <strong>String<\/strong> types specifically.\u00a0 The results were something of a mixed bag and contained some surprises.<\/p>\n<p><!--more--><\/p>\n<h2>The Tests<\/h2>\n<h3><em>Methodology<\/em><\/h3>\n<p>In any discussion of performance testing inevitably methodology comes under scrutiny &#8211; I don&#8217;t intend getting into that discussion &#8211; relative differences are the point of interest here and in terms of the code under test I have tried to be careful to level the playing field as far as possible.\u00a0 Of course, if anybody finds anything that completely invalidates any of the tests, that&#8217;s a different matter entirely.<\/p>\n<p>Compiler settings were the same in all cases\u00a0 Specific settings of note to be mentioned are:<\/p>\n<p>&#8211; For Delphi 2009, the new <a href=\"https:\/\/forums.codegear.com\/thread.jspa?messageID=15238&amp;amp\" target=\"_blank\"><strong>$STRINGCHECKS<\/strong><\/a> compiler setting is <strong>OFF<\/strong>.\u00a0 It is worth noting that this is <strong>ON<\/strong> by default and incurs a performance penalty that is unnecessary unless your Delphi projects also make use of, or are made use of by, C++ Builder.<\/p>\n<p>&#8211; For all versions of Delphi tested, FastMM 4.90 was used.<\/p>\n<p>The memory manager in Delphi 2009 seems actually to be marginally more efficient even than this latest version of FastMM, which was interesting in itself, and for practical purposes of course we would use the built-in memory manager if that is the most efficient.<\/p>\n<p>But for the purposes of comparative performance testing, using the same memory manager with all compilers ensures that results aren&#8217;t influenced by differences in memory manager implementations.<\/p>\n<h3><em>Areas Under Test<\/em><\/h3>\n<p>Andreas&#8217; comments mentioned use of <strong>const<\/strong> parameter declarations specifically, so I looked at these in particular, comparing calls to a procedure with a <strong>const<\/strong> parameter and to a procedure with no <strong>const<\/strong> declaration on the same parameter.\u00a0 Subsequent comments discussing the $STRINGCHECKS setting shed some light on Andreas&#8217; initial findings, but still the results from these tests contained a surprise so I left them in.<\/p>\n<p>Simple tests were contrived for string assignment, concatenation and RTL routines <strong>Copy()<\/strong> and <strong>Delete()<\/strong>.<\/p>\n<p>I included <strong>IntToStr()<\/strong> in order to repeat the tests that previously gave me cause for concern w.r.t ANSI string performance in Delphi 2009.<\/p>\n<p>I also tested using <strong>Pos()<\/strong> to find a substring in a string &#8211; both where the substring of interest did exist within the subject string, and also where it did not.<\/p>\n<p>The final test performed in each compiler version was for the <strong>Replace()<\/strong> function.<\/p>\n<p>A number of additional tests were then run in specific compilers.<\/p>\n<p>For Delphi 7 and Delphi 2007 I also included tests of the FastStrings <strong>FastPos()<\/strong> and <strong>FastReplace()<\/strong> functions.\u00a0 I did not bother including these tests in Delphi 2009 following Andreas comments (confirmed by the tests) about the FastStrings having been superceded by improvements in the RTL.<\/p>\n<p>For Delphi 2009 I included repeats of the various tests using strings declared as <strong>ANSIString<\/strong> &#8211; no explicit conversions to Unicode were involved in any of these tests &#8211; all variables, parameters etc were declared consistently as <strong>ANSIString<\/strong>.<\/p>\n<p>It was suggested that a comparison with <strong>WideString<\/strong> might be of interest.\u00a0 I&#8217;m not convinced that this really is going to provide much insight &#8211; we already know that <strong>WideString<\/strong> is a very inefficient type that is not fundamentally changed in Delphi 2009, and that <strong>UnicodeString<\/strong> is vastly more efficient\u00a0 &#8211; nevertheless I included these tests to satisfy those with curiosity in this area.<\/p>\n<p>Also for Delphi 2009 I incorporated a repeat of the <strong>Concat<\/strong> test using a <strong>TStringBuilder<\/strong>, just to see how performance of this class compared with regular string building operations (specifically concatenation in this case).<\/p>\n<h2>The Tests and The Results<\/h2>\n<p><span style=\"color: #ff0000;\"><em><strong><\/strong><\/em><\/span><\/p>\n<p>[dm]8[\/dm]<\/p>\n<p>Without my Smoketest framework (coming soon, I promise!) this will not compile let alone produce any test results but I provide the source so that anyone can find artefacts in my test code that they feel might explain test results they believe to be inaccurate or unrepresentative.<\/p>\n<p>[dm]9[\/dm]<\/p>\n<p>This isn&#8217;t how Smoketest emits the results by the way (I wish!).\u00a0 The test project is compiled as a CONSOLE app and emits performance results as CSV output to the console which I redirected to files that I then imported into Excel.\u00a0 As a result of this exercise I already have in mind some enhancements to Smoketest to make such comparative testing easier in the future.<\/p>\n<p>For those who have no interest in the complete raw test results, here&#8217;s a capture of the summary tab showing the actual results data for Delphi 2009 and relative comparison of those results with those for Delphi 7 and Delphi 2007 (the result data for which is on separate sheets in the workbook).<\/p>\n<p style=\"text-align: center;\"><a href=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/delphi2009-stringperformance-resultscapture.jpg?ssl=1\"><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-358\" title=\"delphi2009-stringperformance-resultscapture\" src=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/delphi2009-stringperformance-resultscapture.jpg?resize=300%2C280&#038;ssl=1\" alt=\"\" width=\"300\" height=\"280\" srcset=\"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/delphi2009-stringperformance-resultscapture.jpg?w=856&amp;ssl=1 856w, https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/delphi2009-stringperformance-resultscapture.jpg?resize=300%2C280&amp;ssl=1 300w\" sizes=\"(max-width: 300px) 100vw, 300px\" data-recalc-dims=\"1\" \/><\/a><\/p>\n<p style=\"text-align: left;\">The colour scheme should be fairly obvious, and follows a gradient scale from red (worse performance) to green (improved performance).\u00a0 The third column of comparisons shows the relative performance in Delphi 2009 itself of <strong>ANSIString<\/strong> and <strong>WideString<\/strong> against <strong>String<\/strong> (i.e. <strong>UnicodeString<\/strong>).<\/p>\n<h2>First, The Good News<\/h2>\n<p><strong>UnicodeString<\/strong><\/p>\n<p>On the basis of the tested operations, Unicode string performance appears &#8211; with some exceptions &#8211; to be generally only slightly slower than for ANSI strings in Delphi 7.\u00a0 The gap between Delphi 2009 and 2007 is greater however, presumably because Delphi 2007 incorporates improvements over Delphi 7 as Andreas had suggested.<\/p>\n<p>Unsurprisingly it is char-wise operations that suffer most &#8211; <strong>Copy()<\/strong> and <strong>Pos()<\/strong> for example.<\/p>\n<p><strong>ANSIString<\/strong><\/p>\n<p>Some surprising extremes here &#8211; some things are appreciably faster than Delphi 7, but some crucial operations are quite significantly slower and in some cases I&#8217;m at a loss to explain why.\u00a0 The lack of an ANSI version of <strong>IntToStr()<\/strong> hurts badly if you are using <strong>ANSIString<\/strong> explicitly.<\/p>\n<p><strong>WideString<\/strong><\/p>\n<p>No real surprises here.\u00a0 <strong>WideString<\/strong> is slow and performance isn&#8217;t really any different in Delphi 2009 compared to either Delphi 7 or Delphi 2007.<\/p>\n<p>Two notable exceptions to this are <strong>WideChar<\/strong> indexing into a <strong>WideString<\/strong> and the <strong>Replace()<\/strong> function, both of which do seem to be improved compared to Delphi 7, but not Delphi 2007.\u00a0 i.e. these improvements came in Delphi 2007 and remain in Delphi 2009, rather than being a Delphi 2009 improvement per se.<\/p>\n<p><strong>FastStrings<\/strong><\/p>\n<p>Not highlighted in the above image, but available in the test result data, the comparative performance of FastStrings against the RTL routines in Delphi 7 and Delphi 2007 confirmed something that Andreas had suggested &#8211; FastStrings is no longer as generally useful as it once was.<\/p>\n<p>Admittedly my tests only exercised <strong>FastPos()<\/strong> and <strong>FastReplace()<\/strong> as these are the routines most relevant to myself.\u00a0 Of these <strong>FastPos()<\/strong> has clearly been superceded by improvements in the RTL, but not so <strong>FastReplace()<\/strong>, which is still twice as fast as the RTL <strong>StringReplace()<\/strong> routine (but the FastStrings version is admittedly slightly more cumbersome to invoke).<\/p>\n<h2>The Not-So Good News<\/h2>\n<p>As mentioned, overall <strong>UnicodeString<\/strong> performance is comparable to string handling performance in Delphi 7 and somewhat less efficient than Delphi 2007.<\/p>\n<p>There are however a number of cases where the string handling in Delphi 2009 is not just slightly, but significantly worse.<\/p>\n<p>The RTL <strong>Copy()<\/strong> routine is the most obvious and possibly significant example, being fully three times slower than the Delphi 2007 implementation.<\/p>\n<p>The <strong>Pos()<\/strong> routine seems to have suffered quite badly in Delphi 2009, being only half as efficient as that in Delphi 2007.<\/p>\n<h2>The Bad News &#8211; ANSI Strings<\/h2>\n<p>When concerns about the impact of Unicode on applications has been raised, one suggestion has been to &#8220;ANSIfy&#8221; any necessary code.\u00a0 That is, make string (and char etc) declarations explicitly ANSI in order to maintain previous ANSI string behaviour in those areas of an application where is is felt necessary.<\/p>\n<p>Unfortunately if performance is a factor in such cases, this might not fly.<\/p>\n<h1>Conclusion<\/h1>\n<p>First it should go without saying that the significance of any of these results to you will vary according to your application needs.<\/p>\n<p>For myself the findings were predominantly encouraging.<\/p>\n<p>My two greatest concerns with the Unicode implementation were performance and memory footprint.\u00a0 Having adopted UTF16, there is simply no avoiding the memory footprint issue, but it seems that the performance issue is &#8211; to a large extent &#8211; not a significant concern.<\/p>\n<p>But this is does not seem to be necessarily a universal truth.<\/p>\n<p>If performance of string handling code is crucial to your application then it would perhaps be advisable to specifically test your implementation approach and ensure you are getting the most from the compiler.\u00a0 When it was a simple question of <strong>ANSIString<\/strong> vs <strong>WideString<\/strong> it was pretty easy.<\/p>\n<p>With <strong>UnicodeString<\/strong> and <strong>ANSIString<\/strong> it could be more complex.<\/p>\n<p>For my own part, I am no longer too concerned about the FastStrings situation in Delphi 2009, although I might ANSIfy the FastStrings interfaces for my own use just in case, particularly <strong>FastReplace()<\/strong>.<\/p>\n<p>The project for which these concerns were most relevant for me is currently still in Delphi 5, so I suspect that an eventual migration to Delphi 2009 will actually show an overall improvement, relative to that Delphi 5 base (even though we already use FastStrings and FastMM).<\/p>\n<h2>Parting Thought:<\/h2>\n<h2>&#8211; Whereforeart thou TStringBuilder?<\/h2>\n<p>The results for a simple string concatenation using <strong>TStringBuilder<\/strong> were staggering.\u00a0 You will note from the test code that I was careful to not muddy the test results with construction and destruction of the <strong>TStringBuilder<\/strong> itself, but of course in real world usage some additional overhead is bound to be incurred by the need to erect and tear down <strong>TStringBuilder<\/strong> instances as needed.<\/p>\n<p>I haven&#8217;t tested the advantage that <strong>TStringBuilder<\/strong> delivers in Delphi.NET, and if colleagues experiences with the C# equivalent are anything to go by, it could be huge.\u00a0 But this simply doesn&#8217;t seem to apply on the Win32 side and in fact <strong>TStringBuilder<\/strong> seems to exact nothing but penalties &#8211; it results in harder to read code and seems to be slower &#8211; dramatically so &#8211; than &#8220;raw&#8221; string operations.<\/p>\n<p>Certainly, if you were to use <strong>TStringBuilder<\/strong> with performance in mind, it looks like you could be making a terrible mistake, although the test in this exercise hardly constituted a comprehensive test of all <strong>TStringBuilder<\/strong> functionality.\u00a0 Perhaps there are other operations where it is faster.<\/p>\n<p>But it looks to me as if <strong>TStringBuilder<\/strong> is there primarily as a .NET compatibility fixture, rather than to provide any real benefit to developers of Win32 applications, with the possible exception of developers wishing or needing to single-source a Win32\/.NET codebase where string handling performance isn&#8217;t a concern.<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">6<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> NOTE: Downloads are now fixed! Andreas Hausladen generously took the time to make some detailed comments on my previous post, one of which prompted me to throw together some further performance test cases for String types specifically.\u00a0 The results were something of a mixed bag and contained some surprises.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[4,7],"tags":[61,292,62,63,22],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-5D","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":375,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/375\/","url_meta":{"origin":349,"position":0},"title":"Delphi 2009 &#8211; StringPerformance Redux","date":"22 Sep 2008","format":false,"excerpt":"It looks like I may have jumped the gun with my conclusions from the previous exercise to benchmark string performance in Delphi 2009.\u00a0 Following a useful exchange in the comments with Kryvich I corrected a small discrepancy in the tests and made some changes to the performance testing subsystem within\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/delphi2009-stringperformance-chart.jpg?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2185,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2185\/","url_meta":{"origin":349,"position":1},"title":"Smoketest &#8211; Performance Case Visualisations","date":"22 Nov 2013","format":false,"excerpt":"This post is a peek behind the curtain of the next major update to Smoketest which I hope to have completed shortly: Performance Case visualisations. Smoketest has always had two types of test case that you could implement by deriving from two distinct base classes: TTestCase is the base class\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-22-at-09.57.52.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2006,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2006\/","url_meta":{"origin":349,"position":2},"title":"Blowing Smoke&#8230;","date":"01 Nov 2013","format":false,"excerpt":"To alleviate the grind of polishing and sanitising my code (and, let's be honest, just plain 'fixing' it in some cases) ready for release, I have re-kindled my participation on Stack Overflow. In a happy confluence yesterday a question came up which allowed me to exercise one of the libraries\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"Ready to Run","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-01-at-09.00.33.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2144,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2144\/","url_meta":{"origin":349,"position":3},"title":"Smoketest 1.0 &#8211; Release and Be Damned!","date":"14 Nov 2013","format":false,"excerpt":"As I have been promising for some time (quite literally 5 years (!), I am ashamed to admit) I am finally unclenching and releasing the Smoketest framework into the wild, ready or not. The code is published and will continue to be updated in a github repository. Documentation is still\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2013-11-14-at-19.51.57-.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2095,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2095\/","url_meta":{"origin":349,"position":4},"title":"Extending Smoketest (Part 1) &#8211; An Inspector Calls","date":"05 Nov 2013","format":false,"excerpt":"In the soon to be released Smoketest framework it is sometimes useful to create new test types to supplement the tests built-in to the framework. In this and the next post I will walk through the process of implementing and registering a custom test with the Smoketest framework. In a\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":338,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/338\/","url_meta":{"origin":349,"position":5},"title":"Delphi 2009 &#8211; A Heads-Up for Low-Level Coders","date":"13 Sep 2008","format":false,"excerpt":"Prompted by a conversation with some colleagues where-in we collectively speculated about the implementation details of a generic class and what impact - if any - this might have on performance vs a \"traditional\" polymorphic equivalent, I threw together a quick performance test case in my Smoketest framework, and as\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/349"}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=349"}],"version-history":[{"count":17,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/349\/revisions"}],"predecessor-version":[{"id":368,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/349\/revisions\/368"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=349"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=349"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=349"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}