Max/MSP Test 01 :: [send] vs [pattr] vs [forward] vs [pvar]
I’ve been trying out various methods of passing values around my patches and thought I’d put together a patch to demonstrate the performance of each method.
Note: This test was performed on Max 6.1.9 32-bit. Future tests will be performed on the latest Max 7 versions.
First off – there are benefits to each method, so there is often more to consider than the speed (especially when dealing with the pattr system). The [value] object sets are not tested here as they require a [bang] to output, so doesn’t quite fit this testing method.
My test jams 100,000 incrementing values using [uzi] through the given method of remote value transfer and displays the time taken to process each step. Each test uses a [sel 100000] to trigger once the test has been completed.
|METHOD||EXECUTION TIME (ms)|
|Send (streaming value through [number] object)||27|
|Send with routing||26|
|pvar (with [speedlim] @ 1ms on UI object)||6|
|Pattr receive bound (no bindto on send)||319|
|Pattr receive bound (bindto on send)||257|
|Pattr send bound||439|
|Send~ (with [sig~] and [snapshot] @ 1ms)||9|
I suggest downloading the patch and having it open while you read this section. Alternatively, open the image of the test above to follow as you go.
Click on the tabs below to display each category of the test…
These methods simply transfer a value upon receiving one. The last known value is not stored.
This test uses direct patching cable as a control case. This is the standard way to connect things in Max. According to this test, directly connecting objects with patch cables is the fastest method.
This is normally the first “wireless” method of value transfer learnt by people in Max. I would personally use this method for values streaming in high volume if performance is a must.
The [forward] method is slightly different in that it can dynamically change the address it sends to. It is slightly slower than [send] by ~20%. This won’t be an issue most of the time as the numbers are still super low.
I did this test to check how the [number] UI object handles this test. It takes my system approximately 13ms (total time this method takes – time taken for [send] without [number]) / (27 – 14 = 13ms) of CPU time to push 100,000 integers through a Max/MSP [number] object.
We can now easily calculate how long Max takes to process a single update to the [number] object:
<total time taken> / <updates> = <time per update>
13ms / 100,000 = 0.00013ms per [number] UI object update.
This test should be paid close attention to for those wanting to get more out of their patches.
The same as [send]/[forward], only this is to test the added cycles needed to [prepend] the data with an identifier and then check and strip the identifier with [route]. Again, the data is not stored.
I would personally be cautious of pumping lots of differently addressed, high frequency data streams in here (but I totally do sometimes). Although the difference in this test is negligible, I would be very interested in examining how well this method scales with multiple sources and destinations going through the same [send]/[receive] pair.
I find this method very useful for sending “control” or “menu” type messages, primarily from user-interface interactions. It allows me to funnel various functions in to one area.
These methods send a value when received, but also allow the last value to be reiterated by sending a through its inlet.
This object references a UI object with scripting name, updating as frequently as the object. I’m unsure of the way this works behind the scenes in the Max framework, however this is a very impressive method of both transferring a value AND temporarily storing it. None of the previous methods recall its final value, this does.
One issue I had with this test over the others is that it REQUIRES a Max UI object with a scripting name to attach itself to. As seen in the “Send (streaming value through [number] object)” test, UI objects produce overhead when jamming this many values through them – especially when a UI display of the value isn’t needed. So I made another test as an example to show how a UI object’s effect on the patch can be limited via [speedlim]…
Note: This is cheating for this particular test, but is being used here as a demonstration.
What this is doing is preventing the [number] from attempting 100,000 UI updates. Behind the scenes this would:
- Send the first value “1”
- [speedlim] immediately closes a virtual gate for 1ms — meanwhile, uzi keeps ticking.
- 1ms later, [speedlim] allows another value through — uzi has counted up to (100,000 / 5ms) 20,000
- Until it reaches 100,000 ~4ms later
Basically, [speedlim] allows [uzi] to work at full speed, only waiting for a single value to go through to the UI object once every 1ms.
Note that this is really just to highlight how powerful [speedlim] can be to speed up your patches (if you don’t need every value). The overall idea of this patch is to demonstrate how long each method takes to process the entire 100,000 values.
The pattr system is a powerful method of creating variables that can be accessed throughout a patch system of any size. It works similarly to a [send]/[receive] pair, but adds the benefit of storing all of the values within presets that can be stored, recalled, saved and loaded.
For those unfamiliar with the pattr system, I suggest you take a peek at the [pattr] and [pattrstorage] objects within the Max help system. It’s a paradigm definitely worth learning.
Here we have a [pattr] object being fed a steam of data. On the other end of the test is a blank [pattr] object that is initialised on load with (bindto <name>).
Once in the territory of the pattr system, things slow down a lot. Because of this, I would recommend using the pattr methods ONLY when dealing with UI interaction with objects in other patchers.
The major benefit of this method is obviously that the pattr system is designed to easy store the values of objects using the [pattrstorage] object. The added benefit of this is also that pattr objects can be bound to others with neat path addressing through script names. [pattrmarker] is very useful here.
Now – this was a HUGE surprise.
We’ve got the EXACT same method as the previous – only I’ve added a [number] object to the (bindto) outlet of the [pattr] object. As we saw in previous tests, adding a [number] object in the chain should slow things down a lot, but in this case it actually speeds things up by ~19%.
If anyone from Cycling knows the reason why this result is faster than the previous, I’d love to hear why.
Now this was actually a surprise to me. I had no idea that SENDING through a blank [pattr] bound using (bindto) would be slower than sending the data through the other end, receiving on the bound side. Anyone know why this might be?
Again, only use this when dealing with UI interaction with remote/hidden pattr objects.
Again, very similar to Pattr Forwarding, only this is slightly more flexible in that it can be send a (get) message to pull the value. A [prepend] is needed here to add the name of the pattr object before each message, and it is probably due to this process that this method is slightly slower than Forwarding.
This is an unusual case that converts the values into audio signals. The idea here is that the process should be faster since the sending and receiving is done using the audio clock which is very fast. In reality, this doesn’t give any benefit since the data is converted from, and back into standard Max scheduler messages.
Note: This method is dependent on the signal vector settings in the Max settings panel. More tests will be done to illustrate signal vector settings’ effect on this process in future tests.
Now, this test required a slightly modified version of the setup, due to the way I was checking for 100,000. Thought this shouldn’t be effecting the speed in any way.
This test is the most inconsistent in this patch. The result ranged from 5ms to 17ms – with no difference having overdrive on or off.
With the added requirement of requiring DSP to be on and some additional work arounds and patching to make this feasible in a data streaming context, I would most likely avoid this method and simply use [send] or [pvar].