Yes, something like that sounds like it could be the cause. But in that case I guess a better question to figure out might be why the loading of the frameworks even causes a noticeable pause.
Daniel. The first time I run the script included below in Script Geek with no repetitions the timing result is 280 milliseconds. The second time I run the script the timing result is less than a millisecond. I encounter this all the time, and I assumed the difference was attributable to the required frameworks being loaded into memory, although I could easily be wrong about that.
This is also beyond my knowledge level, but I wondered if the time it takes to load the scripting bridge into memory might contribute towards the pause I notice. That’s just a guess, though.
use framework "Foundation"
set theString to current application's NSString's stringWithString:"Hello"
I wondered if the frameworks are freshly loaded into memory every time an ASObjC script is run with FastScripts 3.
I’ve particularly noticed this effect when using Dialog Toolkit Plus. Dialogs that appear instantaneously when run in Script Debugger take a noticeable moment in FastScripts. I had (however anecdotally) attributed this to the above cause.
Interesting.
Which version of macOS are you running?
And if you are using FastScripts, is it v2 or v3?
I think this delay is fairly typical for when the OSA Scripting component loads the necessary bridging files for ASObjC; I’d always noticed it with osascript
(from the command line), which always starts a new process & has to load the bridging headers each time (unlike Script Debugger, which loads it once & reuses the Scripting Component). I’d always avoided ASObjC where possible because of the poor cold-start times.
Note that the slowdown/bridge loading only happens when a line of code calling/accessing ASObjC is actually used (i.e. a use framework "Foundation"
on its own does not cause a slowdown).
Daniel, I’m not sure how FastScripts spawns the separate processes (in advance or on demand), but if the process were to first run an AppleScript similar to the below, that should take care of loading all the ASObjC bridge support prior to actually running a user script.
use framework "Foundation"
use framework "AppKit"
current application's NSString's |string|()
current application's NSWorkspace's sharedWorkspace()
return
(Edited to add AppKit call)
Oh, that’s a great idea. In fact the processes are typically spawned in advance, so that they are ready to go when a script is executed. So that could indeed make a significant impact. I’ll work on a beta update and share it here so people can test it out to confirm.
It might also be a good idea to load AppKit as well, since it’s used for a lot of things (not sure how much extra loading time it adds). I’ve updated the scriptlet above.
Good idea to add AppKit, too. I made an experimental version of FastScripts that does this unilaterally at app launch time for each of the script runners, so it should have invoked a script like @tree_frog shared above by the time any user script is allowed to run in it.
https://redsweater.com/fastscripts/FastScripts3.2.3b3.zip
Let me know if anybody gives it a try, whether it makes any difference at all!
Daniel
First impression is that my Dialog Toolkit Plus-based scripts are now working effectively instantaneously (as they do when run from Script Debugger) whereas previously there was a slight delay (maybe 0.5s on my older machine) when run from FastScripts.
In other words, it’s working. Will report back if there are any issues.
Oh, that’s great. Thanks for the feedback @p1r2c1 … if there doesn’t seem to be any negative impact on scripts that DON’T use ASObjC then I’ll probably include this change in the next official update. Otherwise I’ll try to figure out a way to ensure these “pre-primed” versions of the script runners can be opted in to where appropriate.
Daniel. I installed FastScripts version 3.2.3b3, and the pause I had experienced with the release version of FastScripts 3 is entirely gone. So, for me, FastScripts 3 is now every bit as fast as FastScripts 2. Many thanks
Amazing and wonderful news! Thanks for confirming.
Same here.
Scripts that call libraries using AppleScriptObjC no longer trigger error at launch.
Other scripts behaves normally.
So far…
MacOS 12.6 (21G115)
FastScripts 3.2.3b3 (1730)
Testing from the command line (with wrapper shell scripts), the results speak for themselves.
The test AppleScript I’m using is (called Test.scpt
):
use framework "Foundation"
use framework "CoreWLAN"
current application's CWInterface's interface()'s ssid() as string
Testing with osascript.sh
:
time osascript.sh Test.scpt
real 0m0.261s
user 0m0.181s
sys 0m0.053s
Testing with fastscripts.sh
on FastScripts 3.2.3b2:
time fastscripts.sh Test.scpt
real 0m0.375s
user 0m0.022s
sys 0m0.024s
Testing with fastscripts.sh
on FastScripts 3.2.3b3:
time fastscripts.sh Test.scpt
real 0m0.163s
user 0m0.018s
sys 0m0.021s
So, about a 200 ms speedup with the new beta (the remaining 163 ms is because of how I call the script, not due to FastScripts), making it effectively instantaneous, and way faster than osascript
ever could be.
Thank you so much for going the extra mile to obtain those metrics! I hadn’t ever thought of the possibility of FastScripts as a faster alternative for invoking command line scripts. That’s pretty neat. I guess leveraging the pre-loaded script runner is the big win here. I’ll have to figure out how to promote this better after the update is public
I wonder if it would be a worthwhile enhancement to consider providing a “fastscripts” command line utility that would essentially behave like “osascript” but with all the execution happening via FS instead.
That would be fantastic. I was planning on writing something similar for myself, but just haven’t gotten around to doing it yet.
I ran the above tests above using the little shell script I shared here, which is fairly slow because the boilerplate AppleScript code used to send the Apple Events to FastScripts is compiled on-the-fly.
On my own machine, I use a modified version of that shell script that runs the boilerplate AppleScript from a compiled .scpt file, which is much faster (but harder to explain on the forum). The benefit of this approach is a good balance of speed vs. complexity, since osascript
still handles unpacking the result.
I was planning on tinkering around with writing a proper CLI in Swift for my own use, but it would be a lot more involved than doing the above. That said, an “official” solution would be awesome as everyone would have access & it would be a much more visible feature.
The other option, which may be easier to implement, would be to have FastScripts support “opening” files. That way, you would get free /usr/bin/open
support, and could run scripts with:
open -a FastScripts /PATH/TO/FILE.scpt --args MyCoolArg1 MyCoolArg2
This would also allow scripts to be run from Finder by right-clicking, which would be useful.
The big downside to this approach would be that it may be trickier to return a script result… Script arguments could be handled with the --args
option (and run flags with --env
or parsing of --args
).
Realistically, though, FastScripts can already do all of the above in a roundabout way, so whatever you decide is really just icing on the cake!
Thanks for the encouragement and for the thoughts. I suspect the way I would implement this would be to allow the FastScripts executable to be run from the command line, whether it’s running or not. If it was running then it would essentially act as a call-through to the running copy of the app to dispatch a script execution, and if it wasn’t running it would fire off a script runner just to do the one-time script run. So the “command line tool” would basically be an alias to the app’s executable.
Thanks again to folks here for reporting this. I finally got a chance to finish up the public update for FastScripts 3.2.3, which includes this fix: FastScripts 3.2.3: Better ASObjC Performance - Red Sweater Blog