I have observed that the same piece of code, when placed inside a script object sometimes runs an order of magnitude slower than when outside any script object. Has anyone else seen this??? Here’s an example:
-- Simple handler to calculate a hash value. Outside of any script object.
on calcHash(aKey)
set hashValue to 0
repeat with aCharacter in aKey
set c to ASCII number aCharacter
set hashValue to hashValue + c
end repeat
return ((hashValue mod 1000) + 1)
end calcHash
-- Exact same code inside a script object.
on createTestObject()
script
on calcHash(aKey)
set hashValue to 0
repeat with aCharacter in aKey
set c to ASCII number aCharacter
set hashValue to hashValue + c
end repeat
return ((hashValue mod 1000) + 1)
end calcHash
end script
end createTestObject
-- Scenario #1: Run the handler that is outside the script object
set loopCount to 1000
repeat with i from 1 to loopCount
calcHash("disneyland")
end repeat
-- Scenario #2: Now run the handler via a script object
set myTest to createTestObject()
repeat with i from 1 to loopCount
tell myTest to calcHash("disneyland")
end repeat
I find that Scenario #2 takes about 10x longer to run than Scenario #1.
Anyone have any idea what could be going on here???
My conclusions differ! I ran the script some times, and the same pattern emerged, even before I increased the number of iterations. 15/5000 is 0,03 and 14/5000 is 0,028 so you know how I arrived at the numbers.
The script was run on Snow Leopard using Script Debugger 4.5.7
-- Simple handler to calculate a hash value. Outside of any script object.
on calcHash(aKey)
set hashValue to 0
repeat with aCharacter in aKey
set c to ASCII number aCharacter
set hashValue to hashValue + c
end repeat
return ((hashValue mod 1000) + 1)
end calcHash
script sVersion
on calcAHash(aKey)
set hashValue to 0
repeat with aCharacter in aKey
set c to ASCII number aCharacter
set hashValue to hashValue + c
end repeat
return ((hashValue mod 1000) + 1)
end calcAHash
end script
-- Exact same code inside a script object.
on createTestObject()
script
on calcHash(aKey)
set hashValue to 0
repeat with aCharacter in aKey
set c to ASCII number aCharacter
set hashValue to hashValue + c
end repeat
return ((hashValue mod 1000) + 1)
end calcHash
end script
end createTestObject
set t1 to current date
-- Scenario #1: Run the handler that is outside the script object
set loopCount to 5000
repeat with i from 1 to loopCount
calcHash("disneyland")
end repeat
set t2 to current date
set s1 to t2 - t1
--15,15
-- Scenario #2: Now run the handler via a script object
set t1 to current date
set myTest to createTestObject()
repeat with i from 1 to loopCount
tell myTest to calcHash("disneyland")
end repeat
set t2 to current date
set s2 to t2 - t1
--14,14
set t1 to current date
set myTest to createTestObject()
set calch to myTest's calcHash
repeat with i from 1 to loopCount
calch("disneyland")
end repeat
set t2 to current date
set s3 to t2 - t1
--14,14
set t1 to current date
set myTest to createTestObject()
repeat with i from 1 to loopCount
calcHash("disneyland") of myTest
end repeat
set t2 to current date
set s4 to t2 - t1
-- 15,15
set t1 to current date
set myTest to createTestObject()
repeat with i from 1 to loopCount
calcHash("disneyland") of my myTest
end repeat
set t2 to current date
set s5 to t2 - t1
--14,14
set t1 to current date
set myTest to createTestObject()
repeat with i from 1 to loopCount
calcAHash("disneyland") of sVersion
end repeat
set t2 to current date
set s6 to t2 - t1
--15,15=0,03 0,03/0,028 * 100 = 1.07 ~ 7% slower.
set t1 to current date
set myTest to createTestObject()
repeat with i from 1 to loopCount
calcAHash("disneyland") of my sVersion
end repeat
set t2 to current date
set s7 to t2 - t1
--14,14 / 5000 = 0,028
My conclusion is that as long as something is referenced like from within a script object, then it goes faster. The explanation, as I can recall it is that memory protection and/or validation is then turned off, which speeds up operations considerably. Not so in this case, only 7%
First of all, thank you for taking the time for looking into this and for expanding the test conditions. I change the loop count to 1000, and ran the tests several times. The results were consistently in this range:
Notice that the 7th scenario is off the charts. This seems to differ from what you observed…correct? I wonder what could account for this difference?
Also, do you use Script Debugger? Whereas this set of tests took less than 100 seconds when run from AppleScript Editor, it took 3 minutes! or longer (I gave up after 3 minutes) when run from within Script Debugger. Is it possible that Script Debugger instruments the code so extensively?
Hi. How long has it been since you logged out of your user account? Maybe you should retest after logging out (or restarting).
Using a G4 Mac Mini, running OS 10.4.11, I achieved an average 2 second result (with both handler and object versions) on 2500 iteration runs. Tests were run in Script Editor. I made a few very minor changes”such as removing the handler wrapper from the object”but the essentials are the same:
property loopCount : 2500
set aKey to "disneyland"
global aKey
set theTime to current date
repeat loopCount times
-----------------------------Enable only ONE statement, bounded herein:
calcHash(aKey) --handler
--run Object --object
-----------------------------
end repeat
(current date) - theTime
on calcHash(aKey)
set hashValue to 0
repeat with aCharacter in aKey
set c to ASCII number aCharacter
set hashValue to hashValue + c
end repeat
((hashValue mod 1000) + 1)
end calcHash
script Object
set hashValue to 0
repeat with aCharacter in aKey
set c to ASCII number aCharacter
set hashValue to hashValue + c
end repeat
((hashValue mod 1000) + 1)
end script
This function isn’t that typical, but I definetively learned something from this! That is, for handlers, a reference helps! (Not so with the “end of list construct” for instance).
The other thing that isn’t typical, at least for me, is that scripts are generally, if not paged totally out, not very close, when, and if I rerun a script.
What I am trying to say, that in a typical run time situation, the first time load, and performance is really interesting to measure, because this is what I perceive as speed really, the first time run of a script, and this test doesn’t do that situation justice.
So, maybe in reality, it is faster to use regular handlers, I can’t see that this test has been conclusive in any way regarding that matter. But this is very hard to test, as only the first time counts! The environment is spoiled during the next run, due to having everything in memory, and references there set up, up front.
I can’t understand why this is taking so long time in AppleScript Editor, it could have been fun to see the performance of Smile as well.
I did not run the code in debug mode, but it seems like it does, doesn’t it!
Using Smile: after the first, chrono returns time elapsed since last call to chrono.
-- Scenario #1: Run the handler that is outside the script object
chrono
set loopCount to 1000
repeat with i from 1 to loopCount
calcHash("disneyland")
end repeat
log chrono / loopCount
(*0.003965925791*)
-- Scenario #2: Now run the handler via a script object
set myTest to createTestObject()
log chrono
(*0.175834078691*)
repeat with i from 1 to loopCount
tell myTest to calcHash("disneyland")
end repeat
log chrono / loopCount
(*0.003451762594*)
When it gets really funny is when you put “set myTest to createTestObject()” inside the loop.
If you have debugging on SD uses a different Applescript component, so the times can differ significantly. Depending on the version, displaying the event log or not can make a big difference too.
Putting code in a script object doesn’t make it always faster because you can use script objects in a wrong way. Here is an example when it will improved when using an hash function. This way by calling the hash function by itself and not outside the script object keeps the lines short and therefore it will be executing a bit faster. Also I don’t use ASCII number because that’s deprecated, you could use character id instead. I’m not only executing the hash function faster but also resolving and returning it’s associated values.
set dict to hashTable's instance()'s init()
if dict's setValueForKey("aKey", "Hello World!") then
repeat 1000 times
dict's getValueForKey("aKey")
end repeat
end if
script hashTable
on instance()
script hashTableInstance
property parent : hashTable
property hashTable : missing value
property valueTable : missing value
end script
end instance
on init()
set my valueTable to {}
set my hashTable to {}
repeat 1000 times
set end of my hashTable to {}
end repeat
return me
end init
on setValueForKey(_key, _value)
set x to hashForKey(_key)
considering case
tell item x of my hashTable
repeat with node in it
if item 1 of node is equal to _key then
set item (item 2 of node) of my valueTable to _value
return true
end if
end repeat
set end of my valueTable to _value
set end of it to {_key, count my valueTable}
return true
end tell
end considering
return missing value
end setValueForKey
on getValueForKey(_key)
set x to my hashForKey(_key)
considering case
tell item x of my hashTable
repeat with node in it
if item 1 of node is equal to _key then return item (item 2 of node) of my valueTable
end repeat
end tell
end considering
return missing value
end getValueForKey
on hashForKey(_key)
set _key to _key's id
set sum to count _key
repeat with char in _key
set sum to sum + char
end repeat
return (sum mod 1000) + 1
end hashForKey
end script