Performance overheads

I want to know what the relative performance overheads are for:

Calling a handler in your own script
accessing the standard additions
getting an application to do something.
calling a handler in a different script

With the application, I am not interested in the case where the application needs to be started, only the cost in packaging the apple event, sending it to the application, plus the applications work to unpack the apple event, package the reply and send the replying apple event.

I have a number of performance issues to work out.

First up I have a script that spends most of its time in an application tell block, but also accesses the standard additions quite a lot while in that tell block(I don’t know if that makes a difference or not). The script is also writing to a text file, and is calling a handler in the same script. The script is slower than I would like.

I have also been thinking of writing my own osax because I seem to have the same handlers in most of my scripts. But there isn’t much point if accessing an osax is slower than calling a handler. There is only a small file size cost in having the handlers in each script, just a maintenance nightmare.

Thoughts, or if anybody has actually looked into this and can give some knowledgeable insights that would be great.

I’m far from qualified to answer your questions but maybe you can make use of ASTest to do some benchmarking. ASTest is a framework that enables quick and easy construction of automated tests for putting your scripts through their paces. Maybe the author of ASTest will check in here and offer his input. :wink:

Fast!

If you need it, it doesn’t bother speed performance… You need it!
Anyway, talking about performance & speed, plain AS vanilla uses to be faster than invoking an osax. Time ago I did a test for search/replace routines. If you wish take a look to the results, go to http://homepage.mac.com/julifos/scripts/wis/sr.html
You’ll se that vanilla AS is very fast. But, at this particular thopic, if you’re planning work with large amounts of data or complex data, my experience says “use osaxen”.

Depending on the app… Eg, under OS X Finder is so slow, because it must refresh every time. Also, this does happen (or happened) with FileMaker and other apps.
Anyway, for speed: plain AS > osaxen > apps

Minimal slowest than “in your own script”… See at my test again.

I’d suggest you to make your own test and see what better fit your needs. There are many utilities for speed timing. You can take a look to ScriptBuilders or use the routine posted at (my test again) or make your own ( :wink: )

IIRC, a call to an osax is something like 1/100th the speed of a call to a local handler, and that a call to an application is 1/10th the speed of a call to an osax. There’s overhead in copying the script’s data for dispatch in an Apple Event, and additional overhead when crossing processes as well. The difference between calling a local handler and calling a handler in a local script object (e.g. a library) is trivial, and neither is likely to have a noticable impact on performance compared to the rest of the code anyway. But calling a handler in a separate stay-open applet will be slow, as you’re dispatching Apple Events across processes again.

There’s so many other potential causes for slowdown that have nothing to do with message sending. Best thing to do is to get a timing osax (e.g. GetMilliSec) and do some profiling to identify the bottlenecks. (I see Rob’s suggested ASText, but it doen’t do benchmarking. You’ll have to roll your own code for this, not that basic timing code is hard to do.)

An osax is worth considering if 1. the message dispatch time is trivial compared to the time it takes to churn the data itself, and 2. processing said data takes a significant fraction of the overall running time for the entire script.

The 80/20 rule (one version of it, anyway) states that approximately 80% of processing time is spent executing only 20% of the code. If the code in question is part of that 20%, then performance should be a concern. But if it’s part of the other 80%, then it’s really pretty irrelevant and you should find more important things to worry about.

Absolutely. This is why every half-decent language comes with an ‘import’ command for importing native code libraries. AppleScript (surprise!) does not; you have to roll your own. Static binding is relatively simple to do:

property myLib : load script (alias "path to the myLib script")

...

myLib's doSomething()

Although you have to recompile all dependents whenever the myLib script changes, which can be a bit of a pain if you’ve lots of them. For dynamic binding and/or managing complex dependencies, I suggest you look at ASLoader on my site http://www.barple.pwp.blueyonder.co.uk/. It’s still a bit rough and the documentation and examples mostly stink, but it works and future releases will iron out the kinks and make it more publically presentable. Look at the scripts that use it to get a better idea of what it does. And if you’ve any questions or problems with it then mail me directly and I’ll sort them out.

Thanks for the responses.

Since I am trying to focus on OS X I am not sure how much the timing information for OS 9 is relevant. If there is not much difference then the performance tables are useful.

ASLoader looks really interesting. I looked at ASTest and that does look interesting and it doesn’t look like it would be too difficult to do timing with it. But the calling overhead for an application at something like 1/1000 of the speed of calling a local handler is the type of thing I wanted to know. That means it is sensible to try and limit sending an event to an application as far as possible and try to do as much as possible with each event. Also a library of AppleScripts seems much better value than an osaxen if the job can be done in a handler. Why is an Application so much slower than an osaxen or is this if the Apple Events causes the application to update a window.

The ASLoader stuff seems to be the best possibility. Put a library of all your usual handlers in a known location and have ASLoader load it for you and you get almost the performance of a local handler. Static linking is not good, since soon as the scripts are moved to a different machine they break.

None of this explains quite why my scripts are as slow as they are.

Kevin

It’s designed for a degree of extensibility, so you probably could. But it’d be easier and better to write a dedicated profiling framework from scratch. (Apart from anything else, you really want to profile your script running as a whole, whereas unit testing is about testing individual components.)

As I indicated already, don’t go reading too much into raw percentages: they’re pretty much meaningless by themselves. Say a message dispatch took 1/10th of a second. If it takes 1/100th of a second to do the actual processing and return a result, that 1/10th second is pretty significant. But if the processing time is 10 seconds, it really doesn’t matter if the message dispatch time is 1/10th or 1/1000th of a second as neither makes an appreciable difference to the overall running time.

Practical example: a vanilla ‘ASCII character’ is 20x faster than Standard Additions’ one; the actual conversion is so trivial that other factors such as message dispatching have the most impact. But if you wrote an osax to convert a whole list of numbers then it’d easily outstrip a vanilla handler that does the same one you’ve got more than, say, 100 items; the message dispatch time becomes insignificant in comparison to the actual processing time, and a loop in AppleScript will never match a loop in compiled C for speed.

Static linking is fine for portability, as your libraries are bound to the script at compile-time and will remain there until you recompile it. Object persistency is one of those things that AppleScript does (almost) very well. Portability issues only arise if you need to recompile that script on the new machine. In that situation, you need to install the original libraries onto the new machine and fix the aliases in your script to point to these local copies before recompiling.

ASLoader gives you the flexibility to do dynamic or static linking to libraries and/or support scripts, plus it defines standard [1] naming conventions, folder locations, etc. for libraries to ensure compatibility and portability. The next release will include a script that can autogenerate all the library-loading code for you, so should be pretty painless to use.

Modularity and performance are two separate issues; hopefully this takes care of the former. As far as the latter is concerned, you really need to profile. Until you do, you won’t know if it’s some lousy, inefficient algorithm in your code that needs rewritten; or some lousy, inefficient behaviour in AppleScript that you can possibly work around; or just that the stuff you’re doing is slow and intensive by nature, and can only be sped up by dropping into another language. Once you’ve run some timing tests and identified the major bottlenecks in your code, post back if you need advice on fixing them.

[1] My standards, not Apple’s; but until or unless they decide to create one themselves, it’s the closest thing to a standard that you’re likely to find.

Other things to watch for in AppleScript is lists - especially big ones - and how you manipulate them.

For example, with a large list called myList, this code:

set myList to myList & "new item"

will be much, much slower than:

copy "new item" to end of myList

This is due to the fact that the first format clones the entire myList structure, appends the new item, then changes the reference ‘myList’ to point to the expanded list.

The second form, on the other hand, simply appends the new item to the existing list.

Depending on the size of your list the difference can be significant (several orders of magnitude).

Also, the use of global variables can be quicker than passing parameters between handlers, although you have to balance the memory use of globals (always in ram) vs. parameters (only in ram within the handler)

Finally, for now, file handling can be much slower than holding data in memory and referencing it directly, but without knowing what you’re using the file for it’s impossible to know if this is an issue for you or not.

Code profiling is the only way you’re going to find the slow spots in your code.

Is there an equivalent for strings, so that instead of what I have been doing:

set theString to theString & " end text"

I could achieve what it looks like I am trying to achieve here:

set myString to "The text"
copy "blahblahtext" to the end of myString

to give me
myString “The textblahblahtext”

No. AppleScript’s string/unicode text types are immutable.

Depending on what you’re doing, it may be more efficient to append values to a [slower-growing] list and then coerce the list to text, than to repeatedly concatenate values to a rapidly-expanding string:

on makeLongString1()
	set str to ""
	repeat with i from 1 to 9999
		set str to str & i & return -- string concatenation
	end repeat
	return str
end makeLongString1

on makeLongString2()
	set lst to {}
	repeat with i from 1 to 9999
		set lst's end to i -- list append
	end repeat
	set text item delimiters to return
	return lst as string
end makeLongString2


set t1 to GetMilliSec -- (GetMilliSec osax)
makeLongString1()
set t1 to ((GetMilliSec) - t1) as integer
set t2 to GetMilliSec
makeLongString2()
set t2 to ((GetMilliSec) - t2) as integer

{t1, t2}
--> {13716, 345}

But to write really fast, efficient AppleScripts, you really have to have some insight how AS works internally (always a bit of a gambling game, since implementations can change in future versions; and what’s fast today may be slow tomorrow). Plus a little appreciation of the hard work being done at the lower machine levels in response to these deceptively simple-looking high-level commands (e.g. copying bytes between memory locations every time you concatenate two values) You also need to be a competent program designer and have a basic grasp of algorithms, which is getting into real CompSci territory.

IOW, this stuff is hard. Some day I’ll have to write up some basic theory with examples of the commonest pitfalls in AS (but won’t be soon). A good place to discuss specific problems might be Apple’s AppleScript Users Mailing List - should be some folk there who have CS backgrounds and understand the issues involved.

p.s. ‘set end of theList to someValue’ isn’t really a constant-time operation due to the way lists are [currently] implemented in AppleScript. But it is more efficient than performing the equivalent operation using concatenation and is what you should use.