Script performance: Script Editor vs KM, & app wins vs KM palettes

The two scripts attached are identical except for the parameters defining the type of window they send System Events messages to.

Both run fast in Script Editor, but only one runs fast in KM.

Version one cycles a set of KM palettes around 8 tiling configurations in the corners of the display. Fast execution in both Script Editor and KM.

Version two does the same with TextEdit windows (any multi window app could be specified). Fast in Script Editor, but in KM it appears at first not to work. 20 seconds later it has a delayed effect.

(FWIW Version two also works immediately with TextEdit if launched from Daniel Jalkut's Fastscripts)

cyclePalettesOrWindows-twoVersions.zip (11.7 KB)

textVersionsOfTwoScripts.zip (11.2 KB)

Any thoughts ? Is the script getting big and hitting a stack constraint ? Or is there a sandbox problem ? (The console seems to grumble a bit … )

The shared code was written for a custom plugin, but at the moment it's only proving usable in that form for the palette cycling - other window cycling is too slow from a plugin or Execute action, and needs to be launched from Script Editor, (or FastScripts) where it runs fast.

The delay is in osascript.

You get the same performance if you run the script using osascript in the Terminal (which is how Keyboard Maestro runs all scripts).

osascript ~/Downloads/cyclePalettesOrWindows-twoVersions/cycleTextEditWindows-001.scpt

There is a long pause and then the script runs.

I’m afraid I don’t know why this is or what causes osascript to take so long to run. If you can narrow it down to a simple script that has this long startup when run from osascript, then I can submit it as a bug to Apple which might get a resolution.

It clearly does not affect all scripts, but maybe there is something the script is doing that results in osascript having to set up some sort of environment that takes a long time to setup.

The reason I use osascript for all script execution is that the script system is prone to crashing, long delays on the main event queue, and general poor behaviour, so it is forked off to another process. It is possible I could handle this in some different way, but unlikely for the 7.0 timeframe so it would be good to learn more about when this issue is tickled, and thus perhaps how to avoid it or where Apple should look at resolving it if possible.

Thanks ! That’s really helpful – I’ll see if can narrow it down to something specific.

( My impression is that the delay appeared suddenly, and that the script was run faster by osascript at one stage, so there may well be some particular bottleneck … )

UPDATE

osascript seems to have a problem with running compound whose | where queries on Applescript | JavaScript for Applications object collections.

The following scripts (AS and JXA) execute in a few milliseconds in Yosemite Script Editor, but each takes a full 6 seconds ( 2ms vs 6000+ ms ) on the system here (16 GB 1600 MHz DDR3 mid 2014 Macbook Pro).

~/Desktop $ time osascript osascriptWHOSE_clause_bugAS.scpt
«class pcap» TextEdit, «class pcap» com.apple.appkit.xpc.openAndSavePanelService

real	0m6.118s
user	0m0.023s
sys	0m0.018s


~/Desktop $ time osascript osascriptWHOSE_clause_bugJS.scpt
Application("System Events").applicationProcesses.byName("TextEdit").windows.byName("Untitled 3"), Application("System Events").applicationProcesses.byName("TextEdit").windows.byName("Untitled 2"), Application("System Events").applicationProcesses.byName("TextEdit").windows.byName("Untitled")

real	0m6.109s
user	0m0.056s
sys	0m0.024s

osacriptCompoundWHOSEclauseBug.zip (3.4 KB)

Applescript:

-- VERSION ONE (SIMPLE QUERY) EXECUTES FAST FROM SCRIPT EDITOR AND SHELL OSASCRIPT 
-- tell application "System Events"
-- 	set lstApps to (application processes whose name is "TextEdit") as list
-- end tell

-- VERSION TWO (QUERY WITH AN OR CLAUSE): fast in Script Editor BUT 6 seconds (+ console noise ?) with OSASCRIPT FROM SHELL
tell application "System Events"
	set lstApps to (application processes whose name is "TextEdit" or title is "TextEdit") -- as list
end tell

JavaScript for Applications

//var lstApps = Application("System Events").applicationProcesses.whose({name: "TextEdit"})();

// VERSION TWO (COMPOUND QUERY WITH AN **OR** CLAUSE): **FAST** in Script Editor BUT **SLOW** (+ console noise) with OSASCRIPT FROM SHELL
var lstApps = Application("System Events").applicationProcesses.whose({_or: [{name: "TextEdit"}, {title: "TextEdit"}]})();
	
// More or less simultaneous. Relevant ?
// 09/07/2015 23:16:44.000 kernel[0]: Sandbox: appleeventsd(53) deny file-read-metadata /Library
// 09/07/2015 23:16:44.000 kernel[0]: Sandbox: appleeventsd(53) deny file-read-metadata /Library
// 09/07/2015 23:16:44.000 kernel[0]: Sandbox: appleeventsd(53) deny file-read-metadata /Library
// 09/07/2015 23:16:44.000 kernel[0]: Sandbox: appleeventsd(53) deny file-read-metadata /Library
// 09/07/2015 23:16:44.000 kernel[0]: Sandbox: appleeventsd(53) deny mach-lookup com.apple.ocspd
// 09/07/2015 23:16:44.000 kernel[0]: Sandbox: appleeventsd(53) deny mach-lookup com.apple.ocspd
// 09/07/2015 23:16:44.000 kernel[0]: Sandbox: appleeventsd(53) deny mach-lookup com.apple.ocspd

I have reported the issue to Apple. rdar://21759799.

Thanks for your excellent work.

There does not really look to be much I can do to resolve it. One option is to compile the script as an application, and then use the open command or action to run it instead of osascript.

I could try making my own runner to see if it suffers from the same problem or not, but I can’t really do that at the moment.

1 Like

Calling a compiled version is a clever idea – thanks.

Also, in many cases, I guess one can just replace a complex boolean (whose | where) query with a single clause query followed by further filtering. For a list of open apps with windows, for example, perhaps something like:

(function () {
	var a = Application.currentApplication();
	a.includeStandardAdditions = true;

	var appSE = Application("System Events"),
		lstAppNames = appSE.applicationProcesses.where({
			backgroundOnly: false
		})().filter(
			function (x) {
				return x.windows().length;
			}
		).map(
			function (a) {
				return a.name();
			}
		);

	lstAppNames.sort();

	var maybeChoice = a.chooseFromList(lstAppNames, {
		withTitle: 'Running applications with windows … ',
		multipleSelectionsAllowed: true
	});

	return maybeChoice ? maybeChoice : [];
})();