order of DSP chain in Max5…
I use to be able to predict the order of the DSP chain in Max/MSP4.6
Now in Max5, I have problems, like the one I sent in this thread. Is it due to new features like thread-safe access to buffers?
If anyone can read the following thread and confirm/clarify, that would be appreciated
In my experience it is not wise to make ANY assumptions about the order in which MSP objects will proces based on right-left ordering etc.. However, there is one very safe assumption you can make – an object will not process until all its inputs are processed. So – if one object’s (let’s call in X) output is connected to another objects (let’s call it Y) input you know which order they will process in (X then Y).
Therefore, you can use this to force order of execution by doing things like this:
----------begin_max5_patcher---------- 736.3ocyWtkbaBCEF9Y7pPCO1w0UW3ZeqqiLYxHCxNpEjX.4wNMS7zkRmtT5 RoqjJjL1jDvWhwd7K.5.H8e9z4B77HG2oxUrJWvWA2AbbddjiiwTsAmMicby oqRxnUlGyUvVJm9c2w1aoXqTFyeZciIdpwf9g9bbisYRgRPyYl67sRNMq4Nh E4bQFSYlb7FiETUxibw7GJYIJq3vQ9SfiADBzbBOweL.EMABte2DIWnZlITq Eth+SyBipeUqY6SpdpfYmc2J9bgVTf6qu+KiFUeX7QRjbVUEcN6cHoRIK5.J jgCJdddFZDZXCNr9Hx+jfRb+LYnoAsTsjKRkK6.JdCGTHg3VPIv+V.J8jzjR S5Jswe.gA13+dHSfBN1.ivdgAraX7A73DYdNSndmK+ue8m+9aSrvWpyO.zEo bYGLH3DY.ZeADDS.gu06gvqFDJnB1V4VJWHRYosWgV9a3d8poxxTVYhLSVZ8 I3DRLF4GNVeU31qPnv.O8UnV9VW.IXCPr4FXyHOxAAhUEsi3lNuklPlYq8gO TtxbJWzUVAJ5TC9QwQFgXqRhLpJ.1qa1LSxRtNxkp3RwqL2SOiw.WtNPeHqK j7TRFast7NrCNfGvFHnXhEMA0m78NPlwIVq7bZp1GYzIQp0.nlM5DY.pK9Lf ezgs5I1yTxHBdyimoKlMiUtFjQenfkRyX0k65JHBMbkWQ1Vrd9V7.IGfR3Si RyxjTUcR1TpX9fBK82hvV0hUcEKMjeWBwPFbjonTzsetVg7Gr8iGB7DwCYek h7a8YaggmcSZyK5lwEu8+aLKds8WijJ4hxjFbt4uW.6V9TVkhK11U3tcD.f1 9POxSSYh15KmmVH08G1nAbrW8etPBLMiv5PhlQ8rycrhsNR8fp80dzAE6YJo 5F1GG.udZBeA1SuBR5M6tGNLiXByva9UHO71QmqXIGgZeyF+EGfGSnu+0cO8 Xkz0iR2dJhbgUjdvKi9OFAAV0B -----------end_max5_patcher-----------
You’ve might notice that this patch does exactly the opposite of what you want – the index~ will proces first (this has nothing to do with the position on the screen – the connection of the *~ between the index~ and poke~ is forcing this order.
The problem is that poke~ has no output so you can’t easily do the same the other way round. However – if you put the poke~ in a poly~ eith a dummy out~ you can connect the output of the poly~ to the index~. You can connect a [sig~ 0] to the dummy out~.
This is a little annoying, but 100% reliable. If poke~ had a dummy output this would be a lot easier (feature request c74 ?) This is pretty much the only scenario in which order of execution has ever been an issue for me, but it is is very convenient to be able to control this – I don’t want to have to have a vector of delay so I would use the poly~ trick if I needed to do this.
There is a thread in which I posted a patch using this trick. The message is:
Download the zip file and look at the Transpose2.pfft patch – you’ll see some poly~ objects throughout the patch that are being used to force the order of execution.
Hope this helps,
your safe assumption, with the *~ 0. is working, though I hate to do this kind of dummy thing.
It also explain why my previous patch, which was doing something like in the following, was working and is still working in Max5.
you are, as usual, a wise man. I’ll use your trick to trick my dsp chain.
----------begin_max5_patcher---------- 727.3oc0WtkbaBCEF9Y7pfgG635paXf9VWGYxjQFjcTKHw.xicZl3oKkNcoz kRWIUWvNjXGaBgX29BWNHD++e5niD2OxKXlbMqNv+y9W464c+HOOaHS.ul68 BJnqSyo01lEHXqjy9ZvX2iTr0Ja3OrwGLAsMrXYAWjyT1WA0DbtTnp4emYhA AS.O1V4R01FCah5Botqj4DWPMegflG3ecSCJopza4hE2TwRUt1fhC08pOFCr mhLGgwS.6dGiBDzBaWF7kJtt+ZdBOyFS6rOlDXB8vnQlCi6HUJX00zEr8vRs RV1Ynj7ZYxQoAgPbXvBEjiFguVZfGVZPqTq3hL4pKDTvQnVPYZX+fBoOP4El 3jQS27lgA3HNFYMIAZyFPIVGG8ZcbXebbprnfIT6Y4+7ie86eZyE9jY9gOcY FWdPF.GHFDgsi5gN2C.8CBS6CDJoBV9QM2gswLYUFqpchvgL1zFi4RjQ16H3 VFqRtTjwx1umSk4xJWu.lfSPvvnw5qh1cEDFMknuB1p2ZAinsdZ1hVcEzpi1 G7utGPaAkKN9rhmBMzIV0Xre.WmHdrJCvjXqhcUMgV4OEbXuCi2pMYEWmgSU bovpigqtP5co4rM5p3f2uRkcXMUXB1wiolSgj9MyAgGvJlo5TZkd6FZ1nmJ6 Cun7A4JuhH1ZJw8rxBZ.wyrkymyp13mSuojkQyYlBdf2b40WbJ17bIUYlgMi JVb7bI25sjPGk.39AK3.BK8dQXqawpKZtDFaIBJ1VIJtuy0FxcmTJ+F6T3AO LKRCSBas0rnn94dLns6suaPNW77+ww98MweJRpkKqR2Nh49Y.8Rg6TPFqVwE 6p1e0tOY6FcKOKiIZa1BdVoTu9SiFdgUD6pjziumVSImUIg6fhBOqJh7OmhP +GKI34SRcIUxr8qyIl5fjdFJOojPXxjPyek69cTBZ2cmAwZKXc9FSe1v06PQ T8MOL5u.veOUSB -----------end_max5_patcher-----------
Alex, great post.
I’ve been implementing optimized panner objects designed to work inside poly~ @parallel 1, and order of the DSP chain is a critical issue if I want to design a lockless multi-threaded approach (although I have a locking approach that works extremely well, lockless would just be prettier…). I read this post a year or so ago, and decided to just go with a smart and minimally locking approach, but now I’m at the point where I’d like to at least try lockless, and see if it improves performance.
In your example, you’re dealing with MSP outlets/inlets. I’m wondering if I can use Max outlets and inlets (like with tapin~/tapout~.. tapin~ sends the message "tapconnect" to tap out when dac is toggled). Do you or anyone at Cycling have some insights into how tapin~/tapout~ and this passed message work to ensure dsp chain ordering?
I’m wondering if the tapconnect message triggers an overridden send_dspstate-like method that calls the tapout~ perform routine (not even sure if that is possible)…
Interestingly, I’m find that the DSP chain in less predictable in Max6 than in Max5, when poly~ @parallel 1 is used. For example, I have some multi-threaded send~/receive~-like objects (currently performing ca. 2x better than vanilla send~/receive~ when using many of them inside a poly~). But when I only have a few instances of "senders" inside the poly~, I can clearly see through some debugging information that the dsp chain is often getting reordered on consecutive passes, for example:
sender 1, sender 2, receiver
sender 2, sender 1, receiver
receiver, sender 1, sender 2
The first two don’t bother me, can’t control the order of perform routine completion for instances running in different threads (that’s the point of it). But the third result is bad (the receiver is outside of the poly~, btw). It means that the receiver doesn’t have anything new to read for that pass through the DSP chain.
I don’t get this behavior in Max5, FWIW.
This makes me think some sort of absolute ordering guarantee is really needed, if I want to work in Max6 with my objects. Or I can revert to a more cumbersome locking approach that seems to guarantee order…
Hope someone has some thoughts on a tapin~/tapout~-like setup. I don’t want to use MSP outlets and inlets, for efficiency reasons.
Sorry, that last post was probably noise. I can seemingly cause the above behavior in one of my objects, but it must be due to something idiosyncratic to my code. I spent some time today and came up with a barebones implementation to see if I could reduce the behavior in a way that I could report here (and share the code), but dsp chain is behaving fine there. As a result of that exercise, I’ve basically arrived at a lockless approach (within the perform routines, that is).
I’d still be interested in the idea of using a Max connection between objects to ensure dsp chain order, or more realistically, to ensure the "receiving" object is behind by one signal vector (which is the way tapin~/tapout~ works?).