Optimizing Particle Flow Script Operator For Loops For Speed

MAXScript FAQ > How To Make It Faster > Optimizing Particle Flow Script Operator For Loops For Speed

A user posted this code used inside an On Proceed() handler:

for i in 1 to count do
(
pCont.particleIndex = i
colmult = 80
pspeed = pCont.particlespeed
pvel = sqrt (pspeed.x*pspeed.x+pspeed.y*pspeed.y+pspeed.z*pspeed.z)
pCont.particleAge = pvel*colmult
)

Performance Comments:

The line,

pvel = sqrt (pspeed.x*pspeed.x+pspeed.y*pspeed.y+pspeed.z*pspeed.z)

is equivalent to,

pvel = length pspeed

but is 4 times slower. When calculating inside a Particle Flow operator, you want the fastest method because the code is called thousands and even millions of times, once for each particle.

The first code block evaluates in 3360 ms when run on a particular machine.

(
st = timestamp()
pspeed = [10,2,3]
for i = 1 to 1000000 do
sqrt (pspeed.x*pspeed.x+pspeed.y*pspeed.y+pspeed.z*pspeed.z)
format "%\n" (timestamp()-st)
)

However, the same code using the length() method,

(
st = timestamp()
pspeed = [10,2,3]
for i = 1 to 1000000 do
length pspeed
format "%\n" (timestamp()-st)
)

evaluates in just 813 ms on the same machine, which is 4.13 times faster.

In addition, assigning to a temporary variable pvel adds 200 ms to 1 million calls in both cases, so rewriting the original code block to

(
for i = 1 to count do(
pCont.particleIndex = i
pCont.particleAge = length (pCont.particlespeed) * 80
)
)

without any intermediate variables can shave off up to 600 ms per one million calls on top of the speed up from using the length() method.

Here is another test to simulate this. It contains three variable assignments within the loop and the original velocity calculation:

(
st = timestamp()
for i = 1 to 1000000 do (
pspeed = [10,2,3]
colmult = 80
pvel = sqrt (pspeed.x*pspeed.x+pspeed.y*pspeed.y+pspeed.z*pspeed.z)
pvel * colmult
)
format "%\n" (timestamp()-st)
)

The above code is very similar to what the original code was doing (minus actually reading and writing particle values). It evaluates in 5750 ms.

In contrast,

(
st = timestamp()
for i = 1 to 1000000 do (
pspeed = [10,2,3]
colmult = 80
pvel = length pspeed
pvel * colmult
)
format "%\n" (timestamp()-st)
)

executes in 3200 ms just because we got rid of the three additions and three multiplications within the MAXScript code. Length() does the same internally, but is implemented in C++ and is thus much faster.

The inline version of the test code without any intermediate variables that calculates the same value at the end needs only 1400 ms.

(
st = timestamp()
for i = 1 to 1000000 do (
length [10,2,3] * 80
)
format "%\n" (timestamp()-st)
)

In short, avoid using local intermediate variables within your PFlow scripts and use built-in methods to get the best performance as in:

(
for i = 1 to pCont,Numparticles() do (
pCont.particleIndex = i
pCont.particleAge = length (pCont.particlespeed) * 80
)
)

Previous Tip

For Loop Collect Vs For Loop Do Performance

Next Tip

Using Bsearch For Fast Table Lookup