For Loop Collect Vs For Loop Do Performance
A user asked:
I was quickly making a script to select all objects and I realized that it was very
slow, even with undo off, disableSceneRedraw and suspendEditing.
for o in objects do selectmore o
I thought a bit and discovered that I could select an array of objects like in this
code:
a=#()
for o in objects do append a o
selectmore a
The difference in speed is really huge (like 3000%+ faster).
But there is absolutely no reference to the possibility of selecting an array of objects
in the MAXScript help, even in the "how to make it faster" section.
Is there anything I should know about that way of selecting objects?
Why isn't that documented a tall as it looks very effective?
Answer:
The truth is people tend to code differently.
All the info needed was in the Reference, but it did not really tell you "Don't do
this" because nobody can anticipate every possible expression someone might write.
Doing things one at a time instead of once for the whole collection sounds like it
would be slow, and it is.
The first example loops through an ObjectSet and sets the isSelected property of the
node to true for each one, which is very very slow.
The second example does indeed call selectMore() in a mapped fashion, but the array
building is a bit inefficient, as it uses append() which has to grow the array on
each call by duplicating the previous array in memory. With few objects it would be
negligible though, with a lot more objects it might start causing slowdowns...
POSSIBLE EXPRESSIONS
|
If you really have to do a FOR loop, use the COLLECT form instead of DO. It builds
the array and gives back the result without using append() explicitly or any intermediate
variables to store the array. You could assign to a vatiable if you want to keep the
selection snapshot around.
It would be something like:
|
select (for o in objects collect o)
|
This would allow you to apply where conditions, for example to select all visible
objects,
|
select (for o in objects where not o.isHiddenInVpt collect o)
|
or, if this really is what you are trying to do, selecting ALL scene object, simply
|
|
There is no reason to do selectmore() if you are selecting all scene objects anyway,
and you can operate on the live collection of scene objects without converting it
to array. Note that select() will make sure the previous selection is removed, while
selectMore() will obviously add to the existing one.
SOME BENCHMARK RESULTS
|
The time is measured on the same reference computer around the select() method and
does NOT include the time it takes the viewport(s) to actually redraw, just the time
to set the selected flags of the nodes.
|
EXPRESSION 1 - SELECT ALL
|
|
With 100objects (boxinstances), the above expressiontook31 ms.
|
With 1,000objects, it took 78ms.
|
With 10,000objects, it took 328ms.
|
With 100,000objects, it took 15,468ms.
|
EXPRESSION 2 - DO, APPEND AND SELECT
|
a=#()
for o in objects do append a o
select a
|
With 100, 1000 and 10,000 objects, the above expression performed similarly to the
first one, proving that the memory overhead of the array management is not measurable
with relatively low numbers of iterations.
The code is longer and less elegant though.
|
With 100,000 objects, the expression took 17,547ms., or about two seconds more than
the first expression.
|
EXPRESSION 3 - TEST, COLLECT AND SELECT
|
select (for o in objects where not o.isHiddenInVpt collect o)
|
The above expression took around 430ms with 1000 objects. Performing a condition test on 1000 objects does not make a significant difference,
but increases the flexibility of the expression to select specific objects by their
properties.
|
EXPRESSION 4 - DO AND SELECT MORE
|
for o in objects do selectMore o
|
The above expression took only 171 ms if all 1000 objects were already selected because it had nothing to do, but 674172 ms (over 11 minutes!!!) if none of the 1000 objects were selected!
STAY AWAY FROM IT!
|
Previous Tip
For Loop By Index Vs For Loop Through Collection Performance
Next Tip
Optimizing Particle Flow Script Operator For Loops For Speed