Combining Strings and variables—yet another performance test

 

I’ve been doing a ton of performance testing lately. It’s important to know which methods are the fastest, especially if you’ve been using some of the slowest methods all your life—I’m a victim. Getting in the mindset of “performance first” starts the snowball in improving your apps. This time I’m testing methods for combining Strings, with the hope that I can save a few milliseconds.

Taking a step back to look at memory, Strings are surprisingly heavy. It’s such a commonly used class, you’d think they would be the lightest of them all, but nope. The String class is THE heaviest class in DestroyTwitter, accounting for 40% of its memory usage. The Class class comes in second with 39% and TextField in third with a measly 4%. I always thought BitmapData took the crown, but it limps in fifth with 1.5% just behind the Function class.

Back to speed, we have three of the fastest String combination methods facing off. There’s String+String, String+=String, and String.concat(). I really wish AS3 handled String combination like PHP, allowing embedded variables without multiple Strings or slow methods like StringUtil.substitute(). Because of this, combining Strings and variables involve a ton of quotes and plus signs or a slower String.concat() method. Here are the results followed by the code:

[SWF] Application.swf - 392,771 bytes after decompression
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
+ (100000 iterations)                                                   
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
+                                                           336     0.00
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
+= (100000 iterations)                                                  
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
+=                                                          369     0.00
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
concat (100000 iterations)                                              
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
concat                                                      496     0.00
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
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
package app.test { 
	import com.gskinner.utils.PerformanceTest;
 
	import flash.display.Sprite;
	import flash.utils.setTimeout;
 
	public class Test extends Sprite {
		public var p:PerformanceTest;
 
		public var string:String = "";
		public var firstname:String = "Jonnie";
		public var lastname:String = "Hallman";
		public var adjective:String = "fun";
		public var noun:String = "guy";
 
		public function Test()
		{
			p = PerformanceTest.getInstance();
 
			setTimeout(run, 1000);
		}
 
		public function run ():void {
			var it:uint = 100000;
 
			p.testFunction(stringAddition, it, "+");
			p.testFunction(stringAdditionEquals, it, "+=");
			p.testFunction(stringConcat, it, "concat");
		}
 
		public function stringAddition ():void {
			string = "" +
				firstname +
				lastname +
				"is" +
				"a" +
				adjective +
				noun +
				"to" +
				"be" +
				"around."
		}
		public function stringAdditionEquals ():void {
			string = "";
			string += firstname;
			string += lastname;
			string += "is";
			string += "a";
			string += adjective;
			string += noun;
			string += "to";
			string += "be";
			string += "around.";
		}
		public function stringConcat ():void {
			string = "";
			string.concat(
				firstname,
				lastname,
				"is",
				"a",
				adjective,
				noun,
				"to",
				"be",
				"around.");
		}
	}
}

For Each loops are fast—don’t believe the hype

 

Growing up a young boy in rural Macungie, I was always reminded how slow For In loops are and to avoid them when possible. The frequency of this scarred me so deep, I never trusted any two-worded For loop, until now. Looking over a co-workers code, I noticed he was using a For Each loop to access XML nodes. At first, I was going to bring up (because of the scarring) how For Each loops are slower, but then I decided to test it first—I’ve been burned before. I was startled to see that For Each loops CRUSH For loops when accessing XML. The For Each loop is almost five times faster than my beloved For loop!

This had me thinking—if a For Each loop is so much faster with XML, what about Objects and Arrays? It turns out they are slightly faster, but the difference is negligible. Even so, the amount of typing you save with a For Each loop is easily enough for me to ditch For loops when possible. Of course, there are situations in which a For Each loop simply isn’t an option, so don’t go thinking it’s a replacement.

Below are the results and the code used in the tests. I started using Grant Skinner’s PerformanceTest class—if you don’t have it, pick it up. Also, you might notice I adopted Adobe’s coding conventions. After years of using my own standards and getting weird looks from other developers, I decided to conform. I’m actually happy with the cleaner look.

[SWF] Application.swf - 393,881 bytes after decompression
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forInLoopObject (10000 iterations)                                      
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forInLoopObject                                              23     0.00
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forEachLoopObject (10000 iterations)                                    
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forEachLoopObject                                            11     0.00
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forLoopArray (10000 iterations)                                         
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forLoopArray                                                 13     0.00
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forInLoopArray (10000 iterations)                                       
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forInLoopArray                                               50     0.01
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forEachLoopArray (10000 iterations)                                     
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forEachLoopArray                                             11     0.00
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forLoopXML (10000 iterations)                                           
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forLoopXML                                                  336     0.03
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forInLoopXML (10000 iterations)                                         
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forInLoopXML                                                345     0.03
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
 
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
forEachLoopXML (10000 iterations)                                       
Player version: MAC 10,0,32,18 (debug)
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
method...................................................ttl ms...avg ms
forEachLoopXML                                               70     0.01
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
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
86
87
88
89
90
91
92
93
package app.test {
	import com.gskinner.utils.PerformanceTest;
 
	import flash.display.Sprite;
	import flash.utils.setTimeout;
 
	public class Test extends Sprite {
		public var tester:PerformanceTest;
		public var xml:XML;
		public var object:Object;
		public var array:Array;
 
		public function Test()
		{
			tester = PerformanceTest.getInstance();
 
			xml = 
				<tdd>
					<user>ben</user>
					<user>chris</user>
					<user>james</user>
					<user>jonnie</user>
					<user>omar</user>
				</tdd>;
 
			object = {ben:"ben",chris:"chris",james:"james",jonnie:"jonnie",omar:"omar"};
 
			array = ["ben", "chris", "james", "jonnie", "omar"];
 
			setTimeout(test, 1000);
		}
 
		public function test():void {
			var iterations:uint = 10000;
 
			tester.testFunction(forInLoopObject, iterations, "forInLoopObject");
			tester.testFunction(forEachLoopObject, iterations, "forEachLoopObject");
			tester.testFunction(forLoopArray, iterations, "forLoopArray");
			tester.testFunction(forInLoopArray, iterations, "forInLoopArray");
			tester.testFunction(forEachLoopArray, iterations, "forEachLoopArray");
			tester.testFunction(forLoopXML, iterations, "forLoopXML");
			tester.testFunction(forInLoopXML, iterations, "forInLoopXML");
			tester.testFunction(forEachLoopXML, iterations, "forEachLoopXML");
		}
 
		public function forInLoopObject ():void {
			var user:String;
			for (var key:String in object) {
				user = object[key];
			}
		}
		public function forEachLoopObject ():void {
			for each (var user:String in object) {
			}
		}
		public function forLoopArray ():void {
			var user:String;
			var m:uint = array.length;
 
			for (var i:uint = 0; i < m; ++i) {
				user = array[i];
			}
		}
		public function forInLoopArray ():void {
			var user:String;
			for (var key:String in array) {
				user = array[key];
			}
		}
		public function forEachLoopArray ():void {
			for each (var user:String in array) {
			}
		}
		public function forLoopXML ():void {
			var user:XML;
			var m:uint = xml.user.length();
 
			for (var i:uint = 0; i < m; ++i) {
				user = xml.user[i];
			}
		}
		public function forInLoopXML ():void {
			var user:XML;
			for (var key:String in xml.user) {
				user = xml.user[key];
			}
		}
		public function forEachLoopXML ():void {
			for each (var user:XML in xml.user) {
			}
		}
	}
}

DestroyTwitter 2.0 Preview: Account canvas

 

DestroyTwitter 2.0 Preview: Account canvas

In DestroyTwitter 2.0, the Account canvas gets a major overhaul. As much as it was nice to change your name, bio, and location in 1.x, I found most people only used it once, if at all. I also discovered that more users were interested in seeing their own profile and viewing it in the People canvas made it a pain, constantly needing to reload.

Because of these issues, the Account canvas in 2.0 extends the People canvas. It displays the active user’s info, including API status, in semi-realtime. This means any time you login, tweet, send a DM, receive a DM, follow someone or unfollow someone, this info is updated without using an additional API call. The API status is actually updated any time DestroyTwitter receives any data from the Twitter API without using an additional call. Also, when you click a user’s icon or username in a tweet, if it’s the active account, DestroyTwitter will know to go to the Account canvas rather than the People canvas.

This screenshot does lack a few buttons I plan to add, such as logout, but it’s mostly all done. I’m really excited about these fine-tunings and improvements over 1.x. Do realize I’m working every night to get this out the door. It’s coming along, but will endure a solid private testing period. I’ll make an announcement soon regarding how to sign up. In the meantime, hold tight.

DestroyTwitter 2.0 Preview: Improved item rendering

 

DestroyTwitter 2.0 Preview: Tweet recycling

The biggest feature in DestroyTwitter 2.0 is multiple accounts. Because of this, a serious amount of planning went into memory optimization. I didn’t want usage to double with each account added, so I adopted a few approaches that worked beyond my wildest expectations. One technique I used is improved item rendering. A few weeks back, I wrote a post about scrolling methods. The current one used in DestroyTwitter 1.7.2 uses preallocation and masking. It’s great for scrolling speed, but awful for instantiation speed and memory usage.

In 2.0, I adopted what I call fixed-height smooth recyclable scrolling. Instead of 200 list items for 200 tweets, I have only the visible items and a data list of 200 tweets. So far, it’s proving its worth. In recent testing, I’ve seen a 20mb memory reduction when using three accounts simultaneously in 2.0 compared to one account in 1.7.2. Results like this make me a happy developer.

DestroyTwitter 2.0 Preview: Preferences canvas

 

DestroyTwitter 2.0 Preview: Preferences canvas

Here’s a first glimpse at DestroyTwitter 2.0. Relating to yesterday’s post regarding preferences, I decided to segue into the Preferences canvas. In the current release, this canvas is a bit long-winded and claustrophobic. To combat these issues, I adopted an accordion layout where each set of preferences is expandable and collapsible. Not only does this clean up things a bit, but it also allows me to reuse components, which reduces memory. And finding new ways to reduce memory is like Christmas come early.

DestroyTwitter 2.0 Preview: Differentiating between preferences

 

DT2 Preferences

With multiple accounts in DestroyTwitter 2.0, there are a number of things to consider—one being preferences. In the current release, preferences are global across all accounts. In 2.0, however, some preferences are specific to each account. Above is my plan for divvying them up. Most rest under application, constant across all accounts, but there are a few that might differ from account to account.

Settings like refresh intervals are a given since some accounts require more frequent updates than others while some have canvases that don’t even need to update at all. The ability to disable Quick Friend Lookup when typing “@” is a new preference that is specific to each account. For instance, I want this enabled for my @destroytoday account since I frequently tweet to friends. On the reverse, I want it disabled for @destroytwitter since I only tweet to someone who asks a question or submits feedback.

Over the next few weeks, I’ll write a post previewing the upcoming release. I’m hoping to get it out this month, for private testing at the very least. Check back often for insight into what to expect.

DestroyTwitter on UserVoice

 

UserVoice

I recently signed up with UserVoice to handle feature requests and bug reports. I’m going to give it a shot and see how things go. After answering hundreds of emails each month, I realized many of them pertain to similar suggestions. UserVoice allows users to easily vote for features they want and see status on those already submitted. The service recently provided a year free for Twitter app developers, so why not? Check it out, go wild with requests, and I’ll do my best to stay active on it.

Interview on RIA Radio

 

RIA Radio

The RIA Radio podcast episode that I was a guest on the other week has been posted to the InsideRIA website. It’s a solid hour and a half, so if you plan to listen get a bag of popcorn—I’m a bit long-winded. We discuss my new job at Adobe, how I got started with AIR, the origins of “Destroy Today”, and so much more. Check it out and keep track of how many times I say “you know.”

Redesigning the Apple Mail icon

 

Redesigning the Apple Mail icon

One thing I have always hated about OS X is the Apple Mail icon. It is way over the top, displaying a stamp with a photo in it and the stamped mark saying “Hello from Cupertino.” Johan Prag decided to take things into his own hands with a series called Redesigning OS X on his blog, &seen. It’s simple, identifiable, and beautiful. I, for one, am glad to have it in my dock.

via swissmiss »

Destroy Today now hosted by The Rackspace Cloud

 

Rackspace Cloud

If you can bet on your site being down each month, it’s probably a good indicator to look elsewhere for hosting. Though Dreamhost was a terrific deal, it wasn’t terrific enough to justify the downtime. I started looking around when Brian Connatser sent me a tweet suggesting I jump  on board his cloud. Like 99% of people with web space, Brian happens to have a ton he isn’t using.

I finished the move this morning and I’m hoping it will be smooth sailing from here on out. Thanks again to Brian and those who made a suggestion.