Old methods die hard
A little over a year ago, I wrote a workaround for displaying multiple spaces in HTML TextField instances. The trick is to add to each space. In non-Flash HTML, this would create two spaces for each space, but since the Flash TextField class is unfortunate in so many ways, it ignores the entity, but not completely. Instead of inserting a space, creates an invisible divider, allowing multiple consecutive spaces in an HTML TextField instance.
In that same article, I shared my HTMLFormat.space(n) method that returns n spaces using a for loop. I’ve been using this method for the past few years, not thinking twice about it. Then, last week, I was showing it to a co-worker, saying it was faster than the String.replace() method. I have no idea why I said that, considering I never tested it—I just assumed. Of course, I was embarrassed when it turned out to be five times slower.
I headed to my Test app to find the best approach. Here’s the code I used:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | package app.test { import flash.display.Sprite; import flash.utils.getTimer; import format.HTMLFormat; public class Test extends Sprite { public function Test () { __init (); } private function __init ():void { var $a:uint, $A:uint; var $before:int, $after:int; var $string:String; $A = 10000; $string = "Hey there , what is going on ?"; $before = getTimer (); for ($a = 0; $a < $A; $a++) { $string.replace (" ", " "); } $after = getTimer () - $before trace ("String.replace (\" \", \" \");", $after); $before = getTimer (); for ($a = 0; $a < $A; $a++) { $string.replace (/[ ]/ig, " "); } $after = getTimer () - $before trace ("String.replace (/[ ]/ig, \" \");", $after); $before = getTimer (); for ($a = 0; $a < $A; $a++) { "Hey there" + HTMLFormat.space (2) + ", what is" + HTMLFormat.space (4) + "going on" + HTMLFormat.space (4) + "?"; } $after = getTimer () - $before trace ("HTMLFormat.space (n);", $after); $before = getTimer (); for ($a = 0; $a < $A; $a++) { String ("Hey there" + HTMLFormat.space (2) + ", what is" + HTMLFormat.space (4) + "going on" + HTMLFormat.space (4) + "?") } $after = getTimer () - $before trace ("String (HTMLFormat.space (n));", $after); $before = getTimer (); for ($a = 0; $a < $A; $a++) { ("Hey there" + HTMLFormat.space (2) + ", what is" + HTMLFormat.space (4) + "going on" + HTMLFormat.space (4) + "?") as String } $after = getTimer () - $before trace ("HTMLFormat.space (n) as String;", $after); $before = getTimer (); for ($a = 0; $a < $A; $a++) { "Hey there" + " " + ", what is" + " " + "going on" + " " + "?"; } $after = getTimer () - $before trace ("\" \"", $after); $before = getTimer (); for ($a = 0; $a < $A; $a++) { String ("Hey there" + " " + ", what is" + " " + "going on" + " " + "?"); } $after = getTimer () - $before trace ("String (\" \");", $after); $before = getTimer (); for ($a = 0; $a < $A; $a++) { ("Hey there" + " " + ", what is" + " " + "going on" + " " + "?") as String; } $after = getTimer () - $before trace ("(\" \") as String;", $after); $before = getTimer (); for ($a = 0; $a < $A; $a++) { $string.split (" ").join (" "); } $after = getTimer () - $before trace ("String.split (\" \").join (\" \");", $after); } } } |
And, the startling results:
String.replace (" ", " "); // 15 String.replace (/[ ]/ig, " "); // 142 HTMLFormat.space (n); // 72 String (HTMLFormat.space (n)); // 73 HTMLFormat.space (n) as String; // 75 " " // 15 String (" "); // 15 (" ") as String; // 19 String.split (" ").join (" "); // 178
It looks like your best bet is to use the String.replace () method with a String pattern. Though typing out “ “ is just as fast, it’s an inconvenience for the developer to type out and dirties up the code. As you might have guessed, I will retire the HTMLFormat.space (n) method and spend a solid hour refactoring some code.
First of all, the sigils are freaking me out.
Secondly, I ran your test and got:
With an HTMLFormat.space() of my own recreation:
So I’m seeing similar results. But I have two issues:
1) The HTMLFormat.space() tests are on building a new string every time but the String.replace() is working on the same string each iteration of the loop. Further, the String.replace() will do its job on the first iteration, but then the next 9999 iterations require no actual replaces.
2) The String.replace() method is only for when you know the number of spaces you want at compile time, not at runtime as with the variable n. This is an extra feature of HTMLFormat.space() that makes the comparison less than equal.
Lastly, I made a few tests of my own:
But none of them are even as fast as my above implementation of HTMLFormat.space():
Thanks for the article!
PS: Do you allow code markup for syntax highlighting in your posts?
@Jackson — Since I’m not setting $string to the replaced value, String.replace actually does occur for each iteration. After writing the post, I did realize how it’s more convenient to have an n number of spaces. In the end, I don’t see myself running something like this 10,000+ times. For kicks, I added one more player the game, knowing, however, that it would fail miserably: StringUtil.substitute (str, …rest);.
The code:
and the result:
If you wrap your comment code with <pre lang="actionscript3"> and </pre>, it should format it correctly. At the moment, however, it will be bold because of a lingering CSS style. Since I’m 40,000 feet up, using in-flight wifi, FTP isn’t cooperating for me to fix it. Give me a few hours.