Shark Bites Script

October 17th, 2005

Most Mac developers have, by now, given Apple’s CHUD Performance Tools Suite at least a cursory run-through. These tools, and in particular Shark, are incredibly effective for pinpointing performance problems that may be plaguing your application (or other code).

The presence of these tools has helped many developers inside and outside of Apple produc more efficient code. By effectively painting the low hanging fruit neon green, Shark allows us to easily locate our most embarrassingly slow code – hopefully before anybody else does.

One area of “coding” on the Mac where optimization has been sorely overlooked is that of AppleScript authoring. AppleScript performance can range from instantaneous to mind-bogglingly slow, and usually clocks in at some random level between the two, having been left complete up to chance by the script’s author. While laziness in the past may have been excusable, poor script performance is inexcusable in a post-Shark world.

Chumming Along

When professionals want to lure a shark nearby for viewing or killing pleasure, they may litter the water with visceral mix of blood and flesh: irresistible to the carnivorous beasts. Luckily, attracting Shark to your AppleScript is much easier to stomach, if you know a few tricks.

In AppleScript, as in other code that is executed in linear fashion by a computer, it is frequently most interesting to examine the performance metrics over a quite specific period of time. If you’re experienced with Shark, you may know that a clumsy method of starting and stopping the sampler is provided by way of a keyboard shortcut. This is useful, but often not precise enough for gauging the minute differences between scripting choices. By taking advantage of the the often-overlooked chudRemoteCtrl tool, which can be used to (among other things) remotely start and stop sampling in the main Shark application, we can get fairly precise performance details which are furthermore broken down by process, a detail which can be especially useful in assigning blame.

Preparing Shark for Duty

We can put control of Shark literally at our fingertips, but as the CHUD tools do have some eccentric behaviors, there are a few steps which must be followed to prepare Shark for reliable, scripted obedience:

  1. Launch Shark
  2. Ensure that Shark is set up for “Remote” control. This is off by default but can be turned on by selecting the “Programmatic (Remote)” menu item from the Sampling menu.
  3. Ensure that all sampling settings are to your liking. You probably want to target “Everything” as opposed to a specific process, because a given AppleScript is liable to enlist the help of many processes to achieve its goals. If your Shark window looks more or less like this, then you’re probably in good shape:

  4. Optional: Since sifting through the large quantity of data Shark produces can be daunting, it may be helpful to quit as many running Applications as you can before running your tests.

The power of Shark is now “cocked and ready to fire.” Next we’ll look at what we need to do from an AppleScript to take advantage of this potential.

Unleashing the Beast

Programmers familiar with using Shark from languages like Objective-C or C++ may be familiar with the CHUD library calls that allow procedural starting and stopping of Shark’s profiler. While AppleScript doesn’t include this functonality, we can take advantage of AppleScript’s shell scripting interface to invoke a powerful tool: chudRemoteCtrl. By passing the appropriate arguments to this tool, we can easily start and stop the profiler during the execution of a script, while giving meaningful descriptions to the associated sample sets that are generated as a consequence.

To demonstrate the power of this technique, I will use Shark to profile the performance of an extremely trivial example script. This scripts purpose is to compute the 64th power of 2, through methods ranging from completely idiotic to downright obvious. I chose this example simply to give you dramatically different performance numbers to compare. In real life, the numbers may not always be as “no-brainer,” but the same principles apply.

on run
	-- Run some tests
	set powersOfTwo to 64
	
	my SharkStartSampling("Shell Scripted Dumb Calculator")
	set worstResult to 2
	repeat (powersOfTwo - 1) times
		set badScript to "echo '" & worstResult &"* 2' | bc"
		set worstResult to do shell script badScript
	end repeat
	my SharkStopSampling()
	
	my SharkStartSampling("AppleScript Built-In Dumb Calculator")
	set betterResult to 2
	repeat (powersOfTwo - 1) times
		set betterResult to betterResult * 2
	end repeat
	my SharkStopSampling()
	
	my SharkStartSampling("AppleScript built-in expression")
	set bestResult to 2 ^ powersOfTwo
	my SharkStopSampling()
	
	my SharkStartSampling("Null Operation")
	my SharkStopSampling()
end run

on SharkStartSampling(sampleLabel)
	do shell script "chudRemoteCtrl -s " & quoted form of sampleLabel
end SharkStartSampling

on SharkStopSampling()
	do shell script "chudRemoteCtrl -e"
end SharkStopSampling

Paste the above code into your script editor and, after double-checking that Shark is “ready”, run the script. It’s important to keep Shark happy because it seems to misbehave after unexpected events like attempting to start sampling twice without ending the previous sample. If you get into trouble, you have to quit and relaunch Shark, putting it back into the prepared state described above.

As the script runs, you’ll see Shark processing samples and popping up analysis windows – one for each of the named tests I included in the script. When the script is complete, take a look at the resulting data sets. The first thing that strikes me as I look at the results on my PowerMac G5, is that the dumbest of all the code takes at least 10 times longer to execute than the smartest. As you look at the results in Shark, you can focus on particular processes and apply the powerful data mining utilities to eliminate uninteresting stack calls. For instance, in a real world situation, you will often only be interested in the samples obtained from the program your script was executed in, and the program your script is “telling” to do something (if applicable).

As you glance through the resulting sample sets, you may notice that even the smartest technique comes up with a significant number of samples. There is some overhead to the profiling mechanism, and being a multi-tasking system, the other processes on your computer may be stealing time away from your test. For this reason, I included as a final example a “null operation” which simply starts and stops sampling as quickly as possible. On my computer, the total difference between the control case and the “built-in expression” case is only 15.7ms. By comparison, the dumbest case clocks in at a whopping 8 seconds!

In Summary…

Clearly this example was an extreme case of low hanging fruit, but the principles can be applied in any script to obtain information about its effect on any process in the system. You may also find yourself using this technique to verify or dismiss suspicions about the software you use. What exactly happens when an application asks the system to beep? Is perl faster than ruby? Does iTunes hesitate just a bit longer than usual when asked to play a Britney Spears song? Only one way to find out…

An Update…

On the AppleScript-Users mailing list, Has makes some interesting points about this approach. He points out that, because Shark doesn’t gather data about the particular AppleScript subroutines that time is spent in, it can’t really be described as profiling the script. While I think this is a fair point, I maintain that it can be very useful to figure out where the system is spending time while executing your script. The nice thing about Shark is that it will represent samples from code in any process that was invoked to fulfill the demands of your script. True, if your script is 100% AppleScript and doesn’t invoke any scripting additions that reach the system APIs, then the profile might be kind of boring, but that is in itself informative as well.

Kool and the Nano Gang

October 16th, 2005

With all the hooplah about iPod Nano’s vulnerabilities: scratching, cracking, etc., I admit I’ve become a little more coddling of precious than I might otherwise be. I have owned exactly two iPods in my life: the 5GB original that I bought for half-price a majillion years ago as an Apple employee, and the 4GB iPod Nano that I bought a few weeks ago as an enthusiastic endorsement of my former employer’s kick-ass sense of product design. Maybe in another few years I’ll spend even more money for a 3GB version!

My first iPod, I remember, was purchased as a glorified hard drive. “I’ll never listen to music on this thing,” I thought, “but I can’t argue with a $200 5GB hard drive.”

That’s how Apple won me, an enthusiastic music lover, over to the iPod. God only knows how they get the rest of America to sign on! Anyway, after a few weeks of using my iPod for extremely exciting things like storing mixes of my own acoustic guitar home recordings, I thought I might actually put some pre-recorded tunes on it. You know, something forward-thinking, like songs to work-out to! I put a couple albums on the thing and walked over to the Apple fitness center to test it out.

“Hey! What do you know, this thing really rocks!” I thought to myself (thankfully, because that would sound really embarrassing if I said it out loud). I was grooving to some Built To Spill or something, pushing 3 miles or so on the treadmill as Judge Judy (always on at the Apple fitness center at 3:30 or so) tried to intrude through my (already replacement black) headphones.

As everybody in the room was distracted by their respective entertainment choices, I sensed a slight tugging at my arm. What was that? I looked down instantly to see my spanking new iPod being lifted out of the little storage space on the treadmill, launching into the air as my spasmatic arm caught the headphone wire.

This is where, in a movie, you might hear a voice, lowered in pitch due to the slow motion effect, screaming “NOOOOOO.” I watched my poor little iPod bounce out of its holster, and fall to the speeding treadmill track below. I then jumped off the track, only to look behind me and watch as my precious little device shot out behind me about three feet, while half the gym looked on in alarmed dismay.

Taking it in stride, I walked over to the little (big, by today’s standards) chunk of plastic and picked it up. It looked OK. My headphones still hung around my neck. I plugged them in. “The Plan Keeps Coming Back Again!” I heard Doug Martsch bellow. Not only had my iPod survived, but it was still playing! I jumped back on the treadmill and finished my workout, a satisfied customer.

Last week I finally got the nerve up to take my precious iPod Nano to the gym. Going to the gym these days means riding my bike down to the Cambridge YMCA, so it feels a bit more awkward to be flashing crazy tech gadgets. I got on the treadmill, plugged in my headphones, and started jogging. This time, as it happens, I was listening to a somber George Jones album. I looked around the Cambridge Y as I ran and tried to imagine the sorry words I was listening to, applied to the lives of those I observed.

Since the iPod Nano is so dang small, and apparently so dang scratchable, I had to carefully place it in a little nook at the bottom of my treadmill’s “magazine cozy.” The Nano sat there undisturbed, next to the boring issue of Running magazine that I will never finish and should donate to the gym (so everybody else can be bored by it). I was about halfway through my run, really enjoying the Nano life, when history repeated itself. I felt a slight tug at my arm, the sign that I’d twitched in such a way to snag the headphone wire just right. Before I could even estimate what was about to happen, my precious iPod Nano appeared in the air before me, almost floating. It proceeded to sink quickly, and before I knew what had happened, it struck the treadmill surface, and as before, shot out like a dart onto the ground behind me.

I felt the gym stand still. Before, it was at Apple. Everybody knew how significant the iPod was, but everybody also already had one. I knew subconsciously that everybody at the Y today was thinking, “that guy has the sexy new iPod.” To see me show such obvious disregard for its safety was more than a little embarrassing. I decided the only thing to do was assume a lackadaisical posture. I slowly disembarked from my machine, strolled the few feet to where my iPod had landed, and picked it up. I carelessly plugged my headphones back into the device, and waited with hopeful anticipation. Nothing.

I panicked for a moment, before realizing that it was no longer 2002. I stared into my iPod Nano and, realizing that it had kindly paused upon removal of my headphones, pressed the play button. George Jones resumed on cue, expressing my sentiments exactly: “Just the thought of losing you, scares me half to death.”

Daring Fireball on “The Life”

October 14th, 2005

An interesting commentary today from John Gruber on independent programmers and the alluring goal of achieving “The Life.

Gruber makes some great points about the not-so-simple criteria that define “ultimate success” for an indie programmer, observing that support responsibilities for one-person shows can be crippling. It’s almost enough to make a developer pray for obscurity. Who am I kidding, of course it isn’t!

In this excerpt, Gruber observes that Brent Simmons of NetNewsWire fame is closer to living “The Life” now as an employee than he was as an independent business owner:

Brent and Sheila get rewarded financially for their success and work over the past two years, and Brent gets to devote his time and energy to programming. By selling his company and working on NetNewsWire as a NewsGator employee, Brent has actually gotten closer to The Life than he was as an indie.

I find this especially striking because in many ways Brent has transitioned to steady employment without (it would seem) giving up the benefits of his independence. He continues to work from home (for better and for worse), is charged with mapping the future progress of NetNewsWire, and most likely received stock in the acquiring company, giving him a vested interest in the continuing growth of his product’s popularity.

Most importantly, he and Sheila don’t have to bear the brunt of *everything* anymore. In many cases an acquisition gives more power to the individuals in the smaller company than it does to any individuals in the larger one. Take Apple’s acquisition of NeXT. The individual employees of NeXT who stayed on at Apple suddenly became about a bazillion times more influential on the future of the Mac than the employees who had been working on the Mac for the past 15 years.

I agree with the gist of Gruber’s analysis – “The Life” is getting hired to keep doing what you’ve been doing, with fewer headaches, better pay, and more influence.

Stealing Seconds Back from Xcode

October 14th, 2005

A lot of people like to complain about how Xcode is, in some ways, too slow. Especially when it comes to compilation time, developers who were familiar with CodeWarrior’s relatively blazing speed are often disappointed at the non-stellar performance of gcc in Xcode.

While we wait patiently for Xcode’s performance to improve, it’s important to make sure that we don’t waste any more time or frustration in the program than we need to. To that end, I am always looking for ways to automate those “repeat them all the time” actions I am liable to take in Xcode.

One of the tedious tasks I often find myself can be summarize in three easy words: “Reveal Active Product.” Unfortunately, the process is slightly less straightforward in all but the simplest of projects. One must first navigate through the Files & Groups tree to find the “Products” group. This group may or may not need to be disclosed to reveal its contents. Then the brain must be asked to do extremely menial work for a few moments as it decides which of the multiple products generated by this project is actually the one you’re interested in now. When these brain cells finally die, you’re left with a mouse right-clicking the item of interest, to locate and select the “reveal in Finder” item.

That’s too much work for me! I do this all the time. I need a keyboard shortcut. So, compliments of FastScripts and the following AppleScript, I am from this day forward using “Ctrl-Cmd-R” as a shortcut in Xcode for “Reveal Active Product.”

-- Get the active target's product as a posix path
tell application "Xcode"
	set myTarget to active target of active project document
	set myFile to product reference of myTarget
	set myPath to full path of myFile
end tell

-- Convert to an alias and ask Finder to reveal
set myAlias to POSIX file myPath
tell application "Finder"
	if exists myAlias then
		activate
		reveal myAlias
	else
		beep
	end if
end tell

If the active target’s product exists, the Finder will reveal it. If there is no such file, you get a beep. Enjoy!