Loopin' Lizards! A Comparison of Techniques for Looping through Properties


Author: Bradley Gabe

Some scripting techniques are faster than others!

Whether you just create the occasional script or whether you're a hardcore custom tool developer, one of the most common tasks is to loop through a subset of property nodes in order to get or set values. The following is a study in a variety of techniques for setting up these loops, along with the times required to run them.

For me, one of the biggest surprises has been the speed difference between the worst and best performers. It's also important to note that the worst performers are also the most commonly used by the scripting community, so hopefully this study can help spread some optimization knowledge. Lastly, some of the best performances come from some outside of the box approaches, details in the comments below.

So pull up a chair, open your favorite gigantic production test scene, copy, paste, and run the JScript code below.

START HERE!!!

var allColl = new ActiveXObject("XSI.Collection");
allColl.items = "*"
LogMessage("Node Count: " + allColl.count);

1-Get property by string address

This is an example of the fastest you can get. Unfortunately, it's not available for all properties, such as visibility or display.

s = new Date();
for(var e = new Enumerator(allColl); !e.atEnd(); e.moveNext()){
	var prop = e.item().kinematics;
}
t = new Date();
LogMessage("Direct Pointer: " + (t-s)/1000);


2-Get property by string address

This technique comes from some of the earliest scripting examples in the SDK docs. As a result, many scripters are still using it, and many coffee breaks ensue :-)

A progress bar has been added out of courtesy.

s = new Date();
pb = XSIUIToolkit.ProgressBar;
pb.maximum = allColl.count;
pb.visible = true;
for(var e = new Enumerator(allColl); !e.atEnd(); e.moveNext()){

	var prop = e.item().properties("kinematics");
	pb.Increment();
        // Alternative, but same results timewise
        // var prop = e.item().properties(0);
}
t = new Date();
LogMessage("By String: " + (t-s)/1000);

3. Using GetValue and a String

This technique is starting to gain in popularity. It's faster than the others, but you also run the risk of raising an error if your object does not exist. To compensate for this, one could use error handling, which will slow down the script.

s = new Date();
for(var e = new Enumerator(allColl); !e.atEnd(); e.moveNext()){
		var prop = GetValue(e.item() + ".kine");
                // Dictionary.GetObject has similar performance as
                // well as error handling issues
}
t = new Date();
LogMessage("By GetValue: " + (t-s)/1000);

4. Get property by collection

This is one of my personal faves. It's about the same speed as #3, except never raises any errors. If the objects in question don't exist, you get an empty collection. Testing the collection is much faster than error handling.

s = new Date();
for(var e = new Enumerator(allColl); !e.atEnd(); e.moveNext()){
    // Create an empty XSI collection
    var oColl = new ActiveXObject("XSI.Collection");

    oColl.items = e.item() + ".kine";

    // Test if property found
    if(oColl(0)) var prop = oColl(0);
}
t = new Date();
LogMessage("By Collection: " + (t-s)/1000);

5. Get property by GUID

Thanks to the addition of the FindObjects command, we can get collections of all objects of a particular family almost instantly using their GUID strings. These strings can be found by selection the property in quesion and browsing near the bottom of the SDK explorer view.

A strange discovery is that quite often it's much faster to grab every object of a type in the scene, then filter out from them based on the subset you want in your script.

XSI Collections are excellent for this type of work. Remember that the RemoveItems() method returns a collection of all items removed. Use this as a filter to catch things.

s = new Date();
// Copy all our scene nodes into a new collection
var compareColl = new ActiveXObject("XSI.Collection");
compareColl.AddItems(allColl);

// Grab All kine props in the scene (This giant string is the kinematics GUID)
var allKineProps = FindObjects("","{D557CF35-57E3-11D2-8B5A-00A024EE586F}");

// For every kinematics prop, compare parents
for(var e = new Enumerator(allKineProps); !e.atEnd(); e.moveNext()){
        // Get the parent of the prop node.
        // If the parent matches our object,
        // then we are pointing at the prop we want!
	var tempColl = new ActiveXObject("XSI.Collection");
	tempColl.Add(e.item().parent);

        // If RemoveItems() returns anything, we have a match!
	if(compareColl.RemoveItems(tempColl).count>0){
		var prop = e.item();
	}
        // This also has the added benefit of removing nodes
        // from the comparison collection with each match.
}
t = new Date();
LogMessage("By GUID Filter: " + (t-s)/1000);

6. Get property by Collection Filter

The good news is that if you don't feel like hunting through the SDK browser to find a particular GUID string, you can still use an empty XSI Collection to do the job, then pretty much follow the same RemoveItems() technique as in example 5.

s = new Date();

// Copy all our scene nodes into a new collection
var compareColl = new ActiveXObject("XSI.Collection");
compareColl.AddItems(allColl);

// Grab All visibility props in the scene
var allVisProps = new ActiveXObject("XSI.Collection");
allVisProps.Items = "*.Visibility";

// For every vis prop, compare parents
for(var e = new Enumerator(allVisProps); !e.atEnd(); e.moveNext()){
        var tempColl = new ActiveXObject("XSI.Collection");
	tempColl.Add(e.item().parent);
	if(compareColl.RemoveItems(tempColl).count>0){
		var prop = e.item();
	}
}
t = new Date();
LogMessage("By Collection Filter: " + (t-s)/1000);

This page was last modified 18:42, 30 Aug 2007.
This page has been accessed 13046 times.

© Copyright 2009 Autodesk Inc. All Rights Reserved. Privacy Policy | Legal Notices and Trademarks | Report Piracy