Cards controls loop?

LiveCode is the premier environment for creating multi-platform solutions for all major operating systems - Windows, Mac OS X, Linux, the Web, Server environments and Mobile platforms. Brand new to LiveCode? Welcome!

Moderators: FourthWorld, heatherlaine, Klaus, kevinmiller, robinmiller

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 4163
Joined: Sun Jan 07, 2007 9:12 pm

Re: Cards controls loop?

Post by bn » Tue Aug 13, 2024 12:29 pm

Hi Stam,

That looks an works very well.

One thing I learned the hard way: Scripts can be password protected

I modified your script to guard against that. Of course other solutions are also possible.

Code: Select all

command getObejcts stackRuggedID, @pObjectListA -- build array with objects & scripts of each stack
   local tStack, tCard, tControlID, tCardIDs
   //stack
   put stackRuggedID  into tStack
   put the long name of stackRuggedID into pObjectListA[tStack]["longOwner"]
   put the short name of stackRuggedID into pObjectListA[tStack]["owner"]
   try
      put the script of stackRuggedID into pObjectListA[tStack]["script"]
   catch e
      if e contains "564" then ## password protected
         put "password protected" into pObjectListA[tStack]["script"]
      else
         put "error: " & line 1 of e into pObjectListA[tStack]["script"]
      end if
   end try
   
   //cards
   --    repeat with x =1 to the number of cards of stackRuggedID
   put the cardIDs of tStack into tCardIDs
   repeat for each line tCardID in tCardIDs
      put revRuggedID(the long id of card id tCardID of tStack) into tCard
      put the long id of tCard into pObjectListA[tCard]["longOwner"]
      put the short name of tCard into pObjectListA[tCard]["owner"]
      try
         put the script of tCard  into pObjectListA[tCard]["script"]
      catch e
         if e contains "564" then ## password protected
            put "password protected" into pObjectListA[tCard]["script"]
         else
            put "error: " & line 1 of e into pObjectListA[tCard]["script"]
         end if
      end try
      
      // controls
      repeat with y = 1 to the number of controls of tCard
         put revRuggedID(the long id of control y of tCard) into tControlID
         put the long name of tControlID into pObjectListA[tControlID]["longOwner"]
         put the short name of tControlID into pObjectListA[tControlID]["owner"]
         try
            put the script of tControlID into pObjectListA[tControlID]["script"]
         catch e
            if e contains "564" then ## password protected
               put "password protected" into pObjectListA[tControlID]["script"]
            else
               put "error: " & line 1 of e into pObjectListA[tControlID]["script"]
            end if
         end try
         
      end repeat
   end repeat
end getObejcts
Kind regards
Bernd

Zax
Posts: 519
Joined: Mon May 28, 2007 10:12 am
Contact:

Re: Cards controls loop?

Post by Zax » Tue Aug 13, 2024 12:37 pm

I just tested your stack, Bernd: it is very very fast (and probably accurate of course).

I haven't tested yours, Stam, but, from a user's point of view, I think it could be handy to have an option (a parameter) to restrict the search to the scripts of all controls in the initial mainStack - and thus not search in "external" scripts like stacksInUse.

Another thing (without having tested it): some controls have very long scripts; don't you think that the array pObjectListA[tControlID]["script"] could become huge?

SWEdeAndy
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 324
Joined: Sat Aug 16, 2008 9:48 am
Contact:

Re: Cards controls loop?

Post by SWEdeAndy » Tue Aug 13, 2024 12:58 pm

For additional examples of how such control/script mapping can be done, see my ScriptDependencies tool. It uses recursive functions to trawl any stacks you choose to include, and builds array maps of controls and their scripts down to handler level (and checks for password protection).
It doesn't concern itself with controls with empty scripts though, so it would need to be modified to get geometry data for all controls.

I've not seen any problems when handling massive arrays with it, so that should be fine in your case too, I believe.
Andreas Bergendal
Independent app and system developer
Free LC dev tools: https://github.com/wheninspace
(WIS_WebDeployHelper, WIS_ScriptDependencies, WIS_BrowserAnimation)
WhenInSpace: https://wheninspace.com

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 4163
Joined: Sun Jan 07, 2007 9:12 pm

Re: Cards controls loop?

Post by bn » Tue Aug 13, 2024 1:01 pm

Zax wrote:
Tue Aug 13, 2024 12:37 pm
I just tested your stack, Bernd: it is very very fast (and probably accurate of course).

I haven't tested yours, Stam, but, from a user's point of view, I think it could be handy to have an option (a parameter) to restrict the search to the scripts of all controls in the initial mainStack - and thus not search in "external" scripts like stacksInUse.

Another thing (without having tested it): some controls have very long scripts; don't you think that the array pObjectListA[tControlID]["script"] could become huge?
Hi Zax,

I hope it is accurate :) in the tests I did it worked and found all occurences of the search string in the scripts of the mainstack and its substacks.
I am a bit obsessed with speed and that is one of the reasons I put my code into one long handler. (within reason). Branching into subroutines is probably better coding practice but is a tad slower.

I would not worry about the size of the array, arrays can handle a lot of data. Scripts are not that long on average.

Kind regards
Bernd

FourthWorld
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 10043
Joined: Sat Apr 08, 2006 7:05 am
Contact:

Re: Cards controls loop?

Post by FourthWorld » Tue Aug 13, 2024 2:45 pm

bn wrote:
Tue Aug 13, 2024 1:01 pm
I am a bit obsessed with speed and that is one of the reasons I put my code into one long handler. (within reason). Branching into subroutines is probably better coding practice but is a tad slower.
By how much?

I haven't benchmarked that in over a decade, but back then I had to run tens of thousands of iterations just to be measurable, many times that to become noticeable.

If things have slowed much since then I'd file an enhancement request for review.
Richard Gaskin
LiveCode development, training, and consulting services: Fourth World Systems
LiveCode Group on Facebook
LiveCode Group on LinkedIn

stam
Posts: 3060
Joined: Sun Jun 04, 2006 9:39 pm

Re: Cards controls loop?

Post by stam » Tue Aug 13, 2024 6:37 pm

Zax wrote:
Tue Aug 13, 2024 12:37 pm
I just tested your stack, Bernd: it is very very fast (and probably accurate of course).

I haven't tested yours, Stam, but, from a user's point of view, I think it could be handy to have an option (a parameter) to restrict the search to the scripts of all controls in the initial mainStack - and thus not search in "external" scripts like stacksInUse.

Another thing (without having tested it): some controls have very long scripts; don't you think that the array pObjectListA[tControlID]["script"] could become huge?
I've no doubt Bernd's is the best solution, I just coded this as an exercise.

@Bernd, you make an excellent point about locked stacks - it never crossed my mind but I'm sure it would have bitten me hard if I was actually using this. Rather than a try/catch block in the getObjects handler, I would probably have just blocked it from happening in the AnalyseStacks handler by changing

Code: Select all

getObejcts the long id of stack tStack, tObjectListA
to

Code: Select all

if the password of tStack is empty then getObejcts the long id of stack tStack, tObjectListA
I'm curious if that would be noticeably slower or not...

@Zax: it's not a search function. The search function could be built on top of this to filter the array etc.
Thinking about it more, I wonder if a better approach might be to not exclude IDE or any other stacks but to add a further key as a tag to categorise the origin of the stacks to analyse.

That way you when a search function is build on top of this it should be possible to interactively include stack types (eg IDE only, no substacks etc etc). That might be a more flexible option. My point being that it probably serves to separate the source array from the search functionality.
Although again, I don't know what the speed implications of this would be...

Regarding size of array - no I'm not worried;) these things are fast.

Zax
Posts: 519
Joined: Mon May 28, 2007 10:12 am
Contact:

Re: Cards controls loop?

Post by Zax » Wed Aug 14, 2024 8:29 am

bn wrote:
Tue Aug 13, 2024 1:01 pm
II am a bit obsessed with speed and that is one of the reasons I put my code into one long handler. (within reason). Branching into subroutines is probably better coding practice but is a tad slower.
I noticed that using subroutines significantly increased execution time. We have to choose between execution speed and ease of code review.

Here is a script that tests execution speeds with and without subroutines:

Code: Select all

local tCount, tTimer, theOldTimer, theNewTimer

on mouseUp
   put fld "Loops" into loopsNumber
   if loopsNumber is an integer then
      oldSchoolTest abs(loopsNumber)
      newSchoolTest abs(loopsNumber)
      ------------- result
      local tResult
      put abs(loopsNumber) && "loops:" & cr & "   Old School:" && theOldTimer && "ms" & cr & \
            "   New School:" && theNewTimer && "ms" into tResult
      answer tResult with "Copy" or "OK"
      if it = "Copy" then
         set the clipboardData["text"] to tResult
      end if
   else answer error "Loops must be an integer."
end mouseUp

private command oldSchoolTest loopsNumber
   put 0 into tCount
   put the milliseconds into tTimer
   repeat loopsNumber
      add 1 to tCount
   end repeat
   put the milliseconds - tTimer into theOldTimer
end oldSchoolTest

private command newSchoolTest loopsNumber
   put 0 into tCount
   put the milliseconds into tTimer
   repeat loopsNumber
      newSchoolTestSub tCount
   end repeat
   put the milliseconds - tTimer into theNewTimer
end newSchoolTest

private command newSchoolTestSub @tCount
   add 1 to tCount
end newSchoolTestSub
The result with my old Intel-i7 MacBook Pro:
1000 loops:
Old School: 0 ms
New School: 1 ms

5000 loops:
Old School: 1 ms
New School: 7 ms

10000 loops:
Old School: 2 ms
New School: 10 ms

20000 loops:
Old School: 4 ms
New School: 15 ms

50000 loops:
Old School: 8 ms
New School: 34 ms
SpeedTest.livecode.zip
(1.45 KiB) Downloaded 441 times

stam
Posts: 3060
Joined: Sun Jun 04, 2006 9:39 pm

Re: Cards controls loop?

Post by stam » Wed Aug 14, 2024 8:47 am

Sure - but in the context of the discussion here, is it likely you'll do more than 1000 loops?
It's faster not to use subroutines - sure - but by < 10 ms in the context being discussed.
Even in 10,000 loops, the difference is in the order of < 20 ms.

Obviously I've you're parsing > 50,000 loops, the differences become more noticeable. But that doesn't seem to apply to the current use-case?

Strikes me as an optimisation to consider if the situation requires it - otherwise I personally would default to readable/maintainable code rather than treat everything as speed race - but that's just me ;)

LCMark
Livecode Staff Member
Livecode Staff Member
Posts: 1232
Joined: Thu Apr 11, 2013 11:27 am

Re: Cards controls loop?

Post by LCMark » Wed Aug 14, 2024 9:26 am

@stam is totally right here - optimization of scripts should always be done relative to what the script is DOING (and after measuring it in context).

The above performance test isn't actually really just measuring the cost of calling private handlers - its measuring the cost of calling private handlers *and* repeatedly updating a reference variable. This is of course slower than just inlining the code, but then the handler being called is doing essentially nothing of use so its kind of a pointless comparison/measurement.

If that handler was doing a lot then the difference would go from being what looks like a 4x slow down to some tiny fraction. For example, making the core operation do something more substantial and the differences start to ebb away.

Here this is updated to add some 'meat' to the core operation (again this is actually measuring handler calls + reference variable use compared to no handler/no reference var use):

Code: Select all

private command oldSchoolTest loopsNumber
   put 0 into tCount
   put the milliseconds into tTimer
   repeat loopsNumber
      repeat for each token tToken in the script of me
         add 1 to tCount
      end repeat
   end repeat
   put the milliseconds - tTimer into theOldTimer
end oldSchoolTest

private command newSchoolTest loopsNumber
   put 0 into tCount
   put the milliseconds into tTimer
   repeat loopsNumber
      newSchoolTestSub tCount
   end repeat
   put the milliseconds - tTimer into theNewTimer
end newSchoolTest

private command newSchoolTestSub @tCount
   repeat for each token tToken in the script of me
      add 1 to tCount
   end repeat
end newSchoolTestSub
On my machine for 10000 iterations I get 686ms vs 697s - so what looked like as much as a 400% slow down is actually a 2% slow down.

This can be mitigated further by changing the repeated use of a reference var in the sub handler *to a single fetch/store*:

Code: Select all

private command newSchoolTestSub @xCount
   put xCount into tCount
   repeat for each token tToken in the script of me
      add 1 to tCount
   end repeat
   put tCount into xCount
end newSchoolTestSub
With that change, to see a difference between the handler vs non-handler cases I needed to increase the iteration count on my machine to 100000 where it was 7193ms vs 7258ms - or a 0.9% slow down.

As Knuth is often quoted as saying - premature optimization is the root of all evil!

Zax
Posts: 519
Joined: Mon May 28, 2007 10:12 am
Contact:

Re: Cards controls loop?

Post by Zax » Wed Aug 14, 2024 10:18 am

stam wrote:
Wed Aug 14, 2024 8:47 am
otherwise I personally would default to readable/maintainable code rather than treat everything as speed race - but that's just me ;)
I agree.
(even when splitting the code into multiple subroutines, I often have trouble rereading old code... but that's another topic: it's the problem of a lack of comments, or too poor comments :roll: )

richmond62
Livecode Opensource Backer
Livecode Opensource Backer
Posts: 10076
Joined: Fri Feb 19, 2010 10:17 am

Re: Cards controls loop?

Post by richmond62 » Wed Aug 14, 2024 10:22 am

The problem about comments is almost always the problem with code.

I code in a different way/style to you, so my notes are written in a way I think you will understand (and you probably won't). 8)

The other problem about speedy code is that it doesn't give you time for a gulp of coffee/tea/beer/medical alcohol. :lol:

FourthWorld
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 10043
Joined: Sat Apr 08, 2006 7:05 am
Contact:

Re: Cards controls loop?

Post by FourthWorld » Wed Aug 14, 2024 7:37 pm

LCMark wrote:
Wed Aug 14, 2024 9:26 am
The above performance test isn't actually really just measuring the cost of calling private handlers - its measuring the cost of calling private handlers *and* repeatedly updating a reference variable.
...
With that change, to see a difference between the handler vs non-handler cases I needed to increase the iteration count on my machine to 100000 where it was 7193ms vs 7258ms - or a 0.9% slow down.

As Knuth is often quoted as saying - premature optimization is the root of all evil!
A valuable lesson indeed.

Test design in any field benefits from isolating the effects of the mechanics of interest.

There was a surprisingly long thread here a couple years back where a fella was complaining about LC Server speed relative to an alternative, which started off with a screen shot of test results - where the test platform he was using noted in bold red type at the top of the report that the LC test was invalid because LC never ran at all, erroring out on init. Once I reproduced his setup and ensured LC Server was running, the actual difference was dismissably small.

Your findings are on par with what I remember when stress-testing subroutine calls back in the day. Good to see they've maintained the same low overhead.
Richard Gaskin
LiveCode development, training, and consulting services: Fourth World Systems
LiveCode Group on Facebook
LiveCode Group on LinkedIn

Post Reply