Solving Difficult Problems with the pattr Object Tutorials
Recently, Gregory and I were talking about a couple of different projects we were working on. They all shared something in common: they used the pattr object (and its related objects) in order to solve very difficult problems.
Most people think of pattr as a way to save presets, but it can be much more. It can be a morphing tool, an “idea storage” mechanism, or a message sending tool. But it can also seem a little daunting to use.
When teaching Max, I used to have a long-winded explanation about how the pattr system worked. Then Gregory created a set of tutorial videos that did a much better job at explaining than I ever have. My teaching now points people to these videos - and I’d like to point you to them as well.
Part One: Here’s a quick and easy way to use pattr objects to store presets.
Part Two: The pattr object provides a versatile way to store and share values.
Part Three: The pattrstorage object can interpolate between presets using various scaling techniques.
by Darwin Grosse on October 20, 2015
Great article! Thank you!
Yep this is a great intro to pattr, students use it quite a bit and it is extremely helpful for developing interactive works, and finding new ideas and outcomes, like when that really really cool sound is somewhere between those really cool sounds.
*Ignore text below, there was something else going on in my patch slowing things down.*
(These objects kick ass but I really wish the pattr system got a performance update. I love how easy it is to use, and interpolation is obviously badass, but it's to the point that I can't use them in a high performance/responsive context.
On a moderately sized patch, loading a preset takes over 500ms (sometimes even a second), and interpolation is equally laggy.)
It's impossible to guess whether you're doing anything overly taxing or exotic. No promises, but if you could provide us with an example patch, I'd be glad to have a look. I don't really see those same problems, but I tend to do the obvious stuff (send/receive rather than pattrforward, not working N-nested bpatchers deep, parsimonious use of UI objects, etc.). Perhaps you can help identify those problems. And you also neglected to say much about platform/OS/version of MSP, etc. That would help, too.
*Ignore text below, there was something else going on in my patch slowing things down.*
My workflow is having an autopattr at every depth, with all objects having scripting names, then one pattrstorage at the very top level with '@savemode 0' set. Those are the only pattr objects being used. There's about 140 total items in the pattrstorage so it's not a huge amount in there.
Most of them are about 2-3 layers deep (mainly UI levels) but the attachment is as deep as it goes.
This is happening on a Mac running the latest Max 7.0.6, and on 10.10 and 10.11 of OSX. It's also happened on every version of Max7 since the initial release. I've not tested it much on Max6 specifically, but have had similar performance issues in the past.
Here's a little video showing what I mean. I'm using a mouse to control it, so that's obviously slower, but it's also happening with a controller/hi changing the presets. There's a solid 1sec delay between picking the new preset and it changing over. I get similar sluggishness with interpolation things, particularly if I do a bunch of movement quickly, it kind of backs/queues up.
I don't want to provide a knee-jerk defense of pattr's performance, but consider that when you restore a preset, there's more going on that simply sending the values to the objects. There's some name-lookup overhead (this is one area where we could potentially offer some performance improvement by caching some information -- a potentially error-prone optimization, which is why it hasn't been done). But most importantly, every value which is restored triggers a chain of Max messages starting at the target object. So if you have 140 number boxes with nothing attached to them, you'll find that recall and interpolation is pretty zippy. If you have 140 number boxes with a bunch of additional patching following each one, each triggered by the value recall, you will see some delays, due to the synchronicity of execution in Max. You could try deferlow after the output of your pattr/UI objects to move the heavy lifting to the next scheduler tick, for instance.
If you want to send us your patcher offline, it would provide a good case for performance analysis.
I don't have any pattr objects (only autopattrs), would it work the same just having it after the autopattr'd UI objects?
I'll send the patch in an email.
An autopattr is just a kind of automatic interface for pattr-talking to the objects in the patcher. The communication involved is (as far as you are concerned) exactly the same.
What you would need to do is place your deferlow after the target objects (the objects the autopattr is 'hosting' for the patcher). There's no way to do that from autopattr object itself.
Rodrigo, I profiled your patcher (which does take a while to switch presets), and here's the critical call stack:
Running Time Self (ms) Symbol Name
3314.0ms 42.8% 3314,0 medianfilter_switch_float
3314.0ms 42.8% 0,0 medianfilter_float
3314.0ms 42.8% 0,0 spectralpeaks_medianmask_float
3314.0ms 42.8% 0,0 get_inharmonicity
3314.0ms 42.8% 0,0 calc_pf_descriptor
3314.0ms 42.8% 0,0 calc_descriptors_non_rt
3314.0ms 42.8% 0,0 typedmess_fun
3314.0ms 42.8% 0,0 outlet_anything
3314.0ms 42.8% 0,0 typedmess_fun
3314.0ms 42.8% 0,0 typedmess
3314.0ms 42.8% 0,0 inlet_anything
3314.0ms 42.8% 0,0 typedmess_fun
3314.0ms 42.8% 0,0 outlet_anything
3314.0ms 42.8% 0,0 typedmess_fun
3314.0ms 42.8% 0,0 typedmess
3314.0ms 42.8% 0,0 inlet_anything
3314.0ms 42.8% 0,0 typedmess_fun
3314.0ms 42.8% 0,0 outlet_anything
3314.0ms 42.8% 0,0 pack_out
3314.0ms 42.8% 0,0 pack_bang
3314.0ms 42.8% 0,0 outlet_bang
3314.0ms 42.8% 0,0 inlet_bang
3314.0ms 42.8% 0,0 outlet_bang
3314.0ms 42.8% 0,0 inlet_bang
3314.0ms 42.8% 0,0 outlet_bang
3314.0ms 42.8% 0,0 inlet_bang
3314.0ms 42.8% 0,0 outlet_bang
3314.0ms 42.8% 0,0 inlet_bang
3314.0ms 42.8% 0,0 outlet_bang
3314.0ms 42.8% 0,0 sel_int
3314.0ms 42.8% 0,0 outlet_int
3314.0ms 42.8% 0,0 trigger_iterate
3314.0ms 42.8% 0,0 trigger_int
3314.0ms 42.8% 0,0 outlet_int
3314.0ms 42.8% 0,0 inlet_int
3314.0ms 42.8% 0,0 outlet_int
3314.0ms 42.8% 0,0 livetext_output
Read from the bottom to the top. 'live.text' is being restored via pattr, it hits a 'trigger' object in a subpatcher, via 'sel', bangs a 'pack' object which sends a list to the descriptors~ object, which is taking a very long time (42-47% of CPU time according to my measurements) to do whatever it's doing. In particular, the function medianfilter_switch_float() is taking all that time. The time required for pattr to perform any communication tasks is negligible compared to the activity in this single, non-C74 object. You might want to contact the developer (Alex Harker?) of descriptors~ and find out if this is a usage or a programming issue. In any case, you can deferlow the output of that live.text object (or before the input to descriptors~) to push the processing to the tick following the preset restore.
Hope that helps.
@Jeremy
Interesting! Thanks for profiling the patch.
None of the effects that are being loaded have descriptors~ in them, so will see if something elsewhere is caught up in the autopattr-ing. There's a bunch of descriptors~ in my patch that I'll need to hunt through. Did the profiling say what subpatch the object was in, or if it was a single or multiple instances of it creating the issue?
I'll drop Alex an email and check to see what's going on with descriptors~.
That's not clear from the profile -- I just see how much processing time is being spent in which function calls (and the stack leading to that call). You'll just need to search for a live.text object connected in the way I described: live.text->trigger->sel->pack->descriptors~, and it looks like that is across a few subpatchers, but it's hard to know for sure. It could very well be 10 live.text objects triggering a message to descriptors~ or only one, I can't tell from the profile data.
Best, Jeremy
Ok, I'll have a dig.
I meant if it's a single descriptors~ that's spiking, or multiple ones, as that narrows down my search to non-duplicated parts of the patch.
Yup, found the issue. A bunch of non-fx parts of the patch were caught up by autopattr, including an offline buffer analysis, which was analyzing a 10minute (empty) buffer each time a preset was loaded....yikes!
I've limited the scope of the pattrstorage and you'll probably not be surprised to hear that things are running quickly/tightly!
(gonna go up and edit my initial critical post to show user error as to not put people off their pattr-ing)
Hey, that's great news. Happy to hear that you got the problem resolved (and that it doesn't require any additional work from me!).
Hehe, thanks for helping me find the issue! And sorry to have stunk up this (helpful) thread!
In looking back on it I've setup other patches in a similar way in the past, so that's been the source of my issue with the pattr system for years, lol.
While it's great and beginners love it, autopattr does soak up everything, and there are times (like the above) where that can be a problem. I've really gotten out of the habit of using it, and now opt instead for plain old pattr objects [if nothing else, it adds a nice "check point" in my process where I look to make sure that everything's listed in the pattrstorage]. Measure twice, cut once.
I find that easy to manage as I just give scripting names to the objects I want tracked. Less (mental) overhead than creating tons of individual pattr objects as you code things.