AS Performance
Volume Number: 12
Issue Number: 5
Column Tag: Applescript
Improving AppleScript Performance 
Avoiding the AppleSloth blues
By Mark Alldritt
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
Why Is AppleScript So Slow, Anyway?
This is the question our customers ask most frequently, and one to which there is no
simple answer. The reason given by Apple is that the Apple event mechanism used by
AppleScript to communicate with other applications has serious performance
limitations under System 7. In my tests, there clearly are limits to the number of
round-trip Apple events you can push through the system per second. However,
there’s something else wrong, apparently, because even AppleScript scripts which
don’t generate any Apple events do not seem to perform as well as similar scripts
written in Frontier.
This article provides a number of tips for improving the performance of your
scripts. Depending on the kind of scripts you are working on, these improvements in
performance may turn out to be dramatic.
Improving Compile Time Performance
Regardless of what script editor you are using, you are bound to get to the point where
you wish scripts would compile faster. Here are some tips for keeping compile times
as short as possible:
• Buy Speed Doubler if you are using a PowerPC-based Macintosh. My
customers have reported a 2 to 4 times improvement in AppleScript
performance with Speed Doubler installed.
• Keep the number of scripting additions to a minimum. Having too
many scripting additions slows AppleScript’s load time, since it must process
that many more files in the Scripting Additions folder. Also, more scripting
additions means more event and class names must be parsed from 'aete'
resources and added to the AppleScript symbol table.
A side benefit of keeping the number of scripting additions to a minimum is that
you are less likely to run into terminology conflicts. This happens when a term
is defined by two different scripting additions.
• Split up your scripting projects. Use the load script command to load
pre-compiled portions of your script project. If you’re using Script Editor,
Script Wizard, FaceSpan or Scripter, it’s probably best to store your
pre-compiled scripts in a known location so that you can use simple Load Script
statements:
property lib1 : load script alias "My HD:Script Libraries:Lib1
Alternatively, you can store your libraries in the same folder as your script
editor, and load them with the following command:
property lib1 : load script "Lib1
If you’re using Script Debugger, you can use its path to me handling to store
your libraries in a folder with your script:
property projectPath :
property ctnr of item (path to me) of
application "Finder" as string
property lib1 : load script (projectPath & "Lib1")
Once you’ve loaded your library, you can access properties and call handlers
stored within it like this:
propertyName of lib1
handlerName(p1, p2, p3) of lib1
• Don’t overdo AppleScript formatting. The formatted text feature of
AppleScript is the only really good way to understand how AppleScript has
interpreted your script during compilation. Unfortunately, the more formats
you use, the slower your compiles will be. The slowdown is caused when your
script editor is forced to cope with growing numbers of style runs as it displays
your script following a compile. The answer is to keep the number of formats
you use to a minimum.
This point becomes more important as the size of your scripts grow. For short
scripts, it’s not really an issue, but for scripts larger than about 100K it can
represent as much as a couple of minutes of the total compile time. A
PowerPC-native script editor helps here (Script Debugger, Script Wizard), but
it’s always going to be an issue.
• Keep background activity to a minimum. All the script editors allow some
level of background activity during compiles. If you keep this to a minimum,
your compiles will be faster. Avoid having the Finder perform folder size
calculations, turn off personal file sharing, etc. If you’re using Scripter, turn
off its “allow background compiles” preferences option. This speeds compiles
dramatically.
Improving Run Time Performance
Probably the most frustrating part of using AppleScript is watching its seemingly
slow progress through your script. Here are some tips and ideas for making scripts
run faster.
• Install Speed Doubler if you are on a PowerPC.
See above.
• Keep the number of Apple events your script generates to a
minimum. Under System 7, Apple event performance is poor. A System 7
application can process at most 60 events per second, with typical performance
more like 20-40 events per second. Reducing the number of Apple events will
significantly speed up your script.
The most common mistake is to iterate over application objects using
AppleScript. Instead, use “whose” clauses to get the server application to do the
iteration for you. (Note: not all scriptable applications support this - complain
to the developers and let them know you want full support for the “object
model”.)
The slow way:
tell application "Finder
repeat with aItem in (items of first window)
if name of aItem ends with ".temp" then
delete aItem
end
end
end
The fast way:
tell application "Finder
delete (items whose name ends with ".temp")
of first window
end
The next error people commonly make is to repeatedly ask for the value of
properties. For example, you might do the following:
tell application "Finder
repeat with aItem in items of window 1
if name of aItem ends with ".c" or
name of aItem ends with ".h" then
-- do something with .c and .h files
end
end
end
This script fragment gets the value of the application’s name property twice. A
faster method is to get the name property once:
tell application "Finder
repeat with aItem in items of window 1
set theFileName to name of aItem
if theFileName ends with ".c" or
theFileName ends with ".h" then
-- do something with .c and .h files
end
end
end
• Avoid using AppleScript’s object-oriented features. AppleScript
supports object-oriented programming to a great extent. I’ve found that handler
calls within object instances tend to be slow (the first handler call is the worst).
Use these features only if they are justified.
• Use properties to store pre-compiled values. If there are values which
are calculated once at the beginning of your script, consider putting these
calculations into property definitions. By doing this, you cause the calculations
to be performed at compile time rather than run time.
This technique can be particularly dramatic if you are using scripting additions
to help with the calculations, since the Apple events used to invoke the scripting
additions are generated at compile time.
• Use scripting additions. Use scripting additions for complex list and text
manipulation. The ACME Script Widgets and other collections of scripting
additions offer tools for text parsing, tokenization, list manipulation and other
functions. It’s almost always going to be faster to use a scripting addition rather
than an AppleScript loop to work with these data structures.
• Execute scripting addition commands within your applet’s process.
As stated above, Apple events sent to other processes for handling are slow
(20-60 events/sec); but events processed within the script applet are handled
much faster, since they are dispatched without involving interprocess
communications and process context switches.
So, in the following example, the current date command (which is a scripting
addition command) is executed within the Scriptable Text Editor’s process rather
than the applet:
tell application "Scriptable Text Editor
make new line at end of first window
with data (current date as string)
end
An improvement in speed would result if you did it this way:
set theData to current date as string
tell application "Scriptable Text Editor
make new line at end of first window
with data theData
end
If you are deep within a tell block, you could deal with the problem this way:
tell application "Scriptable Text Editor
...
make new paragraph at end of first window with
data ((current date) of me as string)
...
end
• Use the right tool for the job. Sometimes you can get dramatic speed
improvements by switching tools. For example, if you’re doing text
manipulation, consider using MacPerl, or BBEdit, or a special-purpose scripting
addition. If you have several tools available, try using each of them to perform
time-consuming parts of your script. See which one is best suited and performs
best.
• Use the right object accessing method. The Object Model is a powerful
thing, and offers great flexibility for the script writer. The problem is that it
allows you to easily create object specifications that can be very difficult for the
server application to resolve.
You can sometimes improve performance greatly by changing the way you access
objects. For example, text editors generally maintain internal data structures to
help them identify the beginning of lines. As a result, line-based object
specifications are resolved much faster than word- or paragraph-based
specifiers, because the application can use the data it already has to speed things
up.
Try using different ways of forming your object specifiers and see which ones
are fastest.
• Code time-consuming logic in OSAXen. As a last resort, you can code
time-consuming portions of your script as a scripting addition. MacTech Magazine 10.1 featured an article on just this subject, and you can look at it
online at http://web.xplain.com/mactech.com/Articles/Vol.10/10.01/
Extend-Applescript.bhtml. Documentation on writing scripting additions can
also be found in Apple’s AppleScript Scripting Additions Guide.
The Final Frontier
The applets (standalone AppleScript applications) produced by Script Editor, Script
Debugger, Script Wizard and Scripter all rely on the standard applet glue code
provided by Apple. This glue code provides a framework in which your script
executes. The standard glue code attempts to be a really good MacOS citizen. To this
end, it yields the CPU to other processes very frequently. One way to improve the
performance of your scripts is to write your own glue code, and yield the CPU less
frequently, or possibly not at all. This change, depending on the frequency of yields,
can cause speed improvements of up to 25%.
The Future
The future of AppleScript is quite bright. Following a period of almost non-existent
support from Apple, there is to be a new version (1.2) released sometime this year.
This new version will be PowerPC-native, and is expected to offer performance
comparable to that of Frontier. Beyond the 1.2 release, there’s Copland. There is
reason to expect that under Copland, the AppleEvent Manager will offer dramatic
performance improvements; supposedly, Apple events will be dispatched directly to
your handlers, not to your app’s event loop, thereby removing a serious speed
bottleneck. And, there are rumours of a completely re-architected post-Copland
AppleScript 2.0. In the meantime, AppleScript continues to prove its worth, and the
techniques in this article should help you see some script speed improvements now.