Slow startup with very large patch - how best to tackle?
Sep 12 2019 | 11:40 pm
I have a very large patch, which includes many abstractions and subpatches. All is working correctly, but it takes a long time (~15 seconds) to launch, whether within the Max environment or when built as a standalone (my desired goal). There are a lot of time consuming processes buried within many of the subpatches.
My main patch itself is not large or complicated, but it loads a bpatcher which in turn loads many of the other problematic subpatchers. I've tried scripting the creation of that bpatcher dynamically, and that does work within the Max environment to allow the main patcher window to display instantly, before the 15 second wait begins (so long as I deferlow the scripting messages).
When building that as a standalone though, the standalone doesn't work because the collective does not include all the patches and files necessary - this is expected because those are created dynamically by my top level patch, and there's no way Max would know to include those files in the collective. So I included those files in the application build script, manually adding those files to the collective. That allows the standalone to work again, but now it's super slow upon startup all over again.
Does anyone have a workaround for this dilemma?
- Sep 13 2019 | 9:41 amGeneral tips that helped me with my big patch which I've been developing for years. You might find some of this useful. In my case the app I created was loading in ~4 minutes; now it is around 30 seconds. Here some tips that helped me a lot:- de-encapsule everything. I had a super tidied-up patch, and my heart was broke when I had to de-encapsulate everything. It will need a day of cleaning up and to make it more readable, but in my case was worth.- remove every unnecessary number/message you use for debugging. (you'll have time to re-add them a la carte when necessary. you don't have to debug everthing at once. Once it works remove *everything* you don't need.- if you have a recurrent abstraction try to slim it down to the core. Just removing a comment or finding a way to make the same routine with less will pay its dividend when you have hundreds of them- if you have a recurring abstraction that is just everywhere, even when you might not' need it, consider reducing their number to something reasonable that you feel confident will suffice. For instance, I had an abstraction that had 400 instances. But I needed at most 100 for every possible idea I had in mind. So I created a poly of 100 and referred to that poly as I needed. That was 300 abstractions less, and it showed.- also consider creating abstraction with scripting as you need those functions within. Basically you don't load them until you need them. If most of the time you don't need them... well, it should save you a lot of loading time I know everything is a tradeoff. You might loose easy debugging, a patch jammed with objects instead of tidied encapsulation, and might even be harder to program it (here I'm referring to the poly structure). But this is what helped me and I couldn't be happier with the loading speed of my patch now.
- Sep 13 2019 | 11:14 amThanks for all the advice! Sadly though, most of them are things that I've covered already... I don't have any encapsulation (most stuff is in subpatches), and I've removed any of the "debugging" kinds of objects you refer to already.The one thing that I can work on is in reducing the number of instances of certain abstractions/subpatches I am using, by getting a bit more creative in my patching. I will try that next.Appreciate your help, thanks again!
- Sep 13 2019 | 2:03 pmThe way I understand Personal_Username's advice about "encapsulation" includes avoiding, as far as possible, subpatches and abstractions.
- Sep 13 2019 | 3:10 pmAh, OK.That said, I'm having a hard time understanding *why* that might cause slowdowns.Perhaps someone from Cycling can weigh in?
- Sep 13 2019 | 3:31 pm@ Dan: you are welcome! @ Jean-Francois: correct. in my personal experience when everything is main level and not either encapsulated nor called by abstraction everything loads quicker. Side-effect is nightmarish maintenance. So things must be balanced.I guess abstractions involve looking for that patch in your hd, multiple times. Hence the difference in load times. But it's just my gut feeling.So, if you already did most of that Dan, I'd recommend you to look into converting recurring abstractions into one poly~ (you can set the perfect number of instances of that poly later on). When you need that algorithm, you just send your input there as well as receiving it when and where you need it.Keep in mind that if your abstraction are not only max-based, but also msp/signal-based you will not only save loading time, but you will also save also quite a lot of CPU, since releasing a poly stops all the cpu cycles there. But I guess you know this already ;-)PS. are you also using [pattrstorage]? Tweaking its functionality also reduce my speed time, if I recall correctly. Not sure what the setting was honestly, but keep that in mind. You have many sub-patches (and can't get rid of them) then optimising your pattrstorage will also be beneficial
- Sep 13 2019 | 6:50 pmthe two things which i find slowing down load time are poly~ and uzi-driven initialisation methods.i solved the latter by only initialize things like table, buffer, coll in the first out of 100 abstractions.
- Sep 13 2019 | 6:52 pmThanks Roman... I do have LOTS of colls in my abstractions, though I specify that they should not look to autoload contents from any file, and the ones that are stored with data in them don't really have very much in them....
- Sep 13 2019 | 6:54 pm@PERSONAL_USERNAME - the patch in question doesn't do anything in the audio domain, it's all Max. And no significant or complex use of pattrstorage, but that is a good suggestion for me to double check on....
- Sep 13 2019 | 11:36 pmAnother thing with pattrstorage: "autorestore" is 1 by default. Set it to 0 to prevent the pattrstorage from looking for a file by itself. I'm usually using lots of screen space and very few levels (bpatchers when needed, but as few abstractions as possible). Yet, that discussion made me think: I've used a number of [thru] objects for convenience/easier degubbing. [thru] is an abstraction, albeit a small one.
- Sep 14 2019 | 12:39 amThanks, and yes, already have my autorestore's set to 0....
- Sep 14 2019 | 4:30 amOther small trick coming to mind: turning off the "parameter mode" for objects that have it on by default - I'm thinking [vst~] for instance.
- Sep 14 2019 | 11:37 amThanks, will check that as well Jean-François.
- Sep 15 2019 | 1:07 pmI find load to be much faster on Macs than on PCs.. If you create one tiny subpatch as a splash screen and keep its window open, it will launch first, then you will be able to see how much difference your other optimizations make to actual load itme.
- Sep 15 2019 | 1:11 pmYes, this has been my approach (splash screen) thus far. At least people know that the app is up and running that way... Part of my challenge with that though is that I desire to have an animation occur on that splash screen (spinning wheel or similar), but all of the approaches I've attempted don't actually animate, when Max is busy initializing the other parts of the patch. I guess I have to try and do something in the high priority queue? Not sure how though...
- Sep 17 2019 | 9:47 pmAh. I think I know the answer to that one. When loadbang fires in the top patch, you can send messages to the splash window, but unless you deferlow everything after it in the init routine, it does get blocked while the rest of the loadbang sequence is completed.Yes it is a very old principal of UI design, if the user is waiting for software to do something, then the software is blamed unless it does something entertaining meanwhile. It doesn't need to be informative. A series of visual changes during the wait is enough to reduce user reactions that too much time has passed.
- Sep 17 2019 | 9:55 pmSo the problem it seems is that it takes a LONG time even to get to the loadbang at the top level patcher. I believe loadbangs begin from the "deepest" patch in the hierarchy, and progress their way out. I have so much "initialization" that needs to occur at the lower levels, it takes forever to get to the top level. Which is why no window even shows on screen for a while... Perhaps I should bury the opening of a window deep down in one of my nested patches at the bottom of the patch hierarchy or something? Does that make sense?
- Sep 17 2019 | 10:02 pmWhat I do is put a wire from the loadbang in the top level patch to everything that needs to be initialized, through a trigger object so it doesnt matter if I move things around. I know 'profesionals' use send/receive lol. The wiring is a very sick habit from designing computer chips lol. In this case though it works because send/receives add delay.
- Sep 18 2019 | 12:13 amYes, I do the same, but my scenario is more complex than that. I've got many, many subpatchers, and just having them be initialized first takes quite a while. See this thread for what I was describing above. Just step 1 in Mattijs' first post takes a long time: https://cycling74.com/forums/loadbang-patcherargs-and-pattr
- Sep 18 2019 | 2:40 am"I guess I have to try and do something in the high priority queue?"high priority? from what i know, not a chance. not with GUI externals anyway and also not with jit/GL.but what about a progress bar with 4-5 steps which will be triggered from the initialisation queue of your app? that´s what they do in most computer games. -110 (tru profesionel but wishout sents)
- Sep 18 2019 | 11:16 amThanks and yeah, have thought about that, or even just text that changes periodically....
- Sep 18 2019 | 1:35 pmfor loadbangs order, I use something found on this forum, It only uses use *ONE* loadbang. The mother of loadbangs. All other actions are performed by load percent_value. which is associated with the number currently being addressed by the mother ;-) Having a look at the patch should clarify things. This is a very good way to have you loadbangs order... well... in order!EDIT: Clearer patch
- Sep 18 2019 | 1:41 pmYes, thanks. Again though, the slowness in my particular patch is not coming from activities related to the loadbangs; it's the work being done *before* the loadbangs are even processed. It's just patch instantiation - what Mattijs refers to in the thread above as "Step 1." I've been able to prove this to myself because a good deal of the delay in startup comes from a group of about 40+ patchers I have, none of which have any loadbangs in them. When I remove them from my project, the startup speed increases significantly...
- Sep 18 2019 | 1:50 pmI see... are those patchers 1) encapsulated or abstraction? 2) as clean as possible? meaning also with no other subpatcher/encapsulation in them? 3) relying on externals? 4) relying on pattrstorages? 5) looking for [serial] communication?
- Sep 18 2019 | 2:05 pm1) They're subpatchers - actual patcher files on disk. (see example of one below). 2) They each include at least one additional subpatcher. Sometimes that subpatcher is used 2 or 3 times within the patch (in the example below, "4to5_byte_convert" is the subpatcher. 3) If you mean 3rd party externals, yes, in the "4to5_byte_convert" subpatcher, two of Peter Elsea's Lobjects are used (Lbit and Lybte). 4) No 5) NoAnd here is the "4to5_byte_convert" subpatch
- Sep 18 2019 | 2:58 pmok, what I would try 1) in "4to5_byte_convert" remove (for testing) every comment, every unnecessary number box or message. I see 4 number boxes, a few messages and lots of comment. Since you have two instances of this in every patch I wouldn't be surprised to see *some* change in speed just by doing that. 2) then de-encapsulate both of them and make them part of the parent patch. 3) If you have a few instances of this parent patch (not sure how many), also a clean-up from comments and number box here would be beneficial. Again, it is all proportionate to the number of instances, and the benefit/burden of removing/adding a tiny element can exponentially decrease/increase your speed timeAfter this you might want to test and iteratively remove elements from your patch to see what is the bottleneck. I'd start with the two externals. Most of the times you really bang your head on the wall until you do the old stupid simple debugging 101: remove one element at the time and see which one is causing the problem.Good luck! ;-)
- Sep 18 2019 | 3:43 pm"I believe loadbangs begin from the "deepest" patch in the hierarchy, and progress their way out." Dan, yes, in my experience, it is a core feature of Max that a patcher / abstraction / bpatcher fires its own [loadbang] before the parent patcher instantiates. If you have many branches, I'm not sure you can guess which of the "deep" loadbangs will trigger first, though. But in a given branch, they will reliably trigger from deepest to parent to parent. So, the [loadbang] in a patch will NOT trigger before all bpatchers' loadbangs have triggered. Triggering your splash screen might use that bit of knowledge.I haven't used patcherargs for a very long long time because the "when" they trigger is not reliable like the succession of loadbangs (or at least was not when I first tried them).
- Sep 18 2019 | 3:49 pmSomething like putting in your deepest abstractions a loadbang -> "open splash screen or make a change in what is displayed", then in your main patch a "close splash screen" at the end of your most parent loadbang sequence.
- Sep 18 2019 | 4:09 pmThanks to you both for your comments and suggestions. Will continue to work at this from a number of angles!
- Sep 18 2019 | 5:47 pm"Dan, yes, in my experience, it is a core feature of Max that a patcher / abstraction / bpatcher fires its own [loadbang] before the parent patcher instantiates. " which is why methods of global control in big projects are not the best idea, because you can only design things like that when all of your own (and possible third party) abstractions also (i.e. already!) make use of this system and have a [r] type of "loadbang" instead of a loadbang object. for some sorts of abstractions this wouldnt even work to build them like that.
- Sep 18 2019 | 5:50 pm" Something like putting in your deepest abstractions a loadbang -> "open splash screen "but how would you know which subpatcher opens first? (yes i know, it is the lowest righmost in the lowest rightmost subpatch, but still)
- Sep 18 2019 | 5:59 pmI wouldn't necessarily, but even just getting it down "deep enough" in the patcher hierarchy might be sufficient?
- Sep 18 2019 | 7:11 pmIndeed, Roman, I think you can't know which "branch" of bpatchers/abstractions will load first. You just know that in a given branch, the loadbangs trigger from deep to surface, and consequently that a given patch or subpatch will not fire its own loadbang (and I believe not even instantiate all of its objects - haven't checked that for a while) before all loadbangs in all of its own subpatchers have fired.
- Sep 18 2019 | 10:23 pmit would be a hilarious dead issue when someone would try to organize the objects in his main patcher from right to left and bottom to top according to the desired load order. and if you´d do that, you still have the problem that you can not come around the first-inner-then-outer order in case there are subs. patching layout should follow your thinking structure; often i follow the layout of my GUI, or create groups of different types of processes . ;)
- Sep 18 2019 | 10:33 pm"I wouldn't necessarily, but even just getting it down "deep enough" in the patcher hierarchy might be sufficient?" you could have a patcher with tons of subs in it, and all of the subpatchers use a receive (or inlet) instead of an own loadbang. now in this case there would be only one main loadbang - and it would not matter when it loads because it executes only after loading the patch anyway, as you know. but what is completely impossible is to control the order of all of these objects´ initialisation process fromn outside - you would have to do it by manually numbering all of the "receives". which then - next problem - will not work for abstractions used more than one time, there it had to be done from an additional abstraction for each of the patches. and what do you do when you number 400 things and then you find out you made an arror at #78 and have to start all over? you hopefully have a replacement mouse and keyboard ready in this case.
- Sep 18 2019 | 10:35 pmwould be fun to make an example project now but tomorrow my calendar is full. :)=
- Sep 18 2019 | 10:43 pmhm :) proposal for best-practice scenario: - all of the patches and objects will get a custom ID at its "receiver", which you note down in a textfile and document what is what. - from the main loadbang, which goes to a [counter], you now use a [coll] to be able to easily change the order of initialisation. that should be it. however, like i said above, it wont work for all of the abstractions you already have made.
- Sep 19 2019 | 3:15 amWell, well. A quick test on my side was not conclusive: it looks like the deep loadbangs still trigger after the whole hierarchy of the patch is instantiated. Another idea maybe: open a small launcher patch, showing a splash screen. This patch then loads the main patch with [pcontrol]. I'm not sure that would fly in a standalone, though.
- Sep 19 2019 | 3:44 amSo, something like that a separate launcher patch works here, but a [metro] connected to a [led] doesn't show anything until my large patch is fully loaded. Already better than other solutions, it shows a splash screen, launches the large patch, then closes itself:
- Sep 19 2019 | 12:06 pmI guess it's not possible to animate the splash screen patch while the same instance of Max is loading a large patch. You could animate / give information from the moment the first loadbang in your large patch triggers. On Mac you get a sort of an animation during the loading: the rainbow wheel is spinning when your mouse is on the splash screen...
- Sep 19 2019 | 12:19 pmWhile we're at this topic, do you have any experience regarding styles vs load time?
- Sep 19 2019 | 12:37 pmYour conclusions are drawing very close to what I've seen as well @Jean-Francois; first, that no loadbangs get executed anywhere until the whole hierarchy of patchers is instantiated. This is consistent with Mattijs' findings references in the thread above that I linked to.Re: your demonstration patch above - I believe the [led] doesn't flash because it's in the low-priority queue. [metro] is high priority I think, but as @Roman mentioned earlier, all normal UI externals run as low priority. I'm referencing Joshua Kit Clayton's wonderful explanation of all of this event priority stuff: https://cycling74.com/articles/event-priority-in-max-scheduler-vs-queue . So the goal would be to figure out some kind of user visible action that occurs in the high priority queue. I think. :-)Still your method may very well be sufficient for my needs - thank you! I was trying to do the same thing with scripting - not including the "slow" hierarchy of subpatchers in my main patch, and rather scripting their creation upon main patch launch. But the pcontrol method seems to essentially do the same thing...
- Sep 19 2019 | 12:37 pmAnd zero experience using styles, sorry!
- Sep 19 2019 | 1:25 pmOne more thing: I got nice results using a [metro 1000] -> [counter] -> [print] to get the loading time displayed (only afterwards, but it's already interesting). Interestingly, [timer] doesn't work with the same precision.One more [deferlow] before giving wclose to [bpatcher] looks nice: that way, you see the app's window underneath the splash screen before it disappears.
- Sep 19 2019 | 1:43 pmActually, with two [deferlow] objects, you can get some activity on the splash screen before loading the main patch (you could even delay loading the main patch with [pcontrol] just for the purpose of showing some "fake but reassuring" progress!) and you get some progress before closing the splash screen, while the main patch is doing its own loadbang or other activity.
- Sep 19 2019 | 2:38 pmVery nice @Jean-Francois, thanks!
- Sep 19 2019 | 3:28 pmReally interesting discussion here!I do have one (terrifying) question. Do comment boxes count as "UI" objects in the same way as number/messages?I'm very meticulous in my patches to not use numbers/messages at all, but I comment the shit out of everything (similarly color coding things).If that's the case that kind of changes how I think about documentation and clean patching...
- Sep 19 2019 | 8:54 pmfun, so with a bunch of deferlows you can get it to work halfways though it shouldnt, haha.or what about script-creating bigger subpatches? then you can have loadbangs drawing naked sheep in your splash screen for 5 minutes before continue to load more. once more, a ridiculous redesign for such a small effect (but i totall understand dan´s wish, such a progress bar for loading patches is on my to do list for many years) isnt there a way how to display anything graphically which does not need a loadbang? so that it can happen before loading has finished without problems?
- Sep 19 2019 | 8:57 pmcomments i dont think so, if nothing is connectd to them they should be dead and rendering should be quick. it doesnt have different states, isnt it?
- Sep 20 2019 | 7:45 amYeah that's what I thought, and why I used them so much.They are all static in my patch too.Phew!
- Sep 20 2019 | 12:06 pm
or what about script-creating bigger subpatches?This has been my approach thus far. Then of course if you are creating a standalone, you have to manually include those files in the standalone's build script, and I've also found that that also seems to slow down initial startup, prior to any loadbangs occurring. I'm guessing because the whole collective still has to get parsed through upon initial startup before anything else occurs? Just speculation here. Would be wonderful to get a definitive C74 comment on the exact sequence of what happens when a standalone is started...
isnt there a way how to display anything graphically which does not need a loadbang? so that it can happen before loading has finished without problems?Again, speculation here, but I don't think *any* window will appear on screen before the initial patch initialization/loading happens, prior to any loadbangs....
Do comment boxes count as "UI" objects in the same way as number/messages?Yeah, I've got to think that you're OK with comments Rodrigo.... at least in my experience they don't add any significant overhead
- Sep 20 2019 | 2:20 pmno you are right, i forgot about building an app. it will only work fo a bunch of patches.