Friday, March 21, 2008

Optimizing WebLink: A Better pfcCreate()

While performance testing a processing intensive WebLink application in Pro/Engineer, I came across something surprising. Use of the PTC supplied pfcCreate() function inside a loop made the application significantly slower.

The bigger the loop, the slower the application. For a small loop, the user probably wouldn't notice, but it is very noticeable when recursively processing 10,000+ part assemblies.

Here's what I was using that could be very slow:

for (int i=0; i<array.Count; i++) {
obj = pfcCreate("pfcBlahBlahBlah");
// do something here with 'obj'
}






I came to the conclusion that getting these objects from the COM server (on windows) was an expensive operation. Minimizing its use seemed to be a very good idea. My first though was to simply pull pfcCreate() out of the loop, which made it much faster. Problem solved!

Faster code:

obj = pfcCreate("pfcBlahBlahBlah");

for (int i=0; i<array.Count; i++) {
// do something here with 'obj'
}







Well, not quite.

The problem is that that chunk of code is used in some other loop, which could be used in some other loop, possibly in some other function, and so on. It was just not enough to rewrite the code in that way. Something else had to be done to eliminate that performance hit.

When it occurred to me that pfcCreate() always returned class objects - the same every time - then I had my answer. Rewrite pfcCreate() and enable caching of the returned objects.

Now, instead of hitting the COM server for every request, the function first performs a lookup of the class object in a global array (creating it if necessary). If the class object has previously been requested, it will be found in the global array, and that object will be returned. If it is not found, the class object is obtained from the COM server, stored in the global array, and then returned to the caller.

In addition to being more efficient when getting WebLink class objects, I now can write my code any way I want putting pfcCreate() inside or outside of the loop, no performance hit either way.


Here is the modified pfcCreate() that I use in my applications:


function pfcCreate (className) {
if (!pfcIsWindows())
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");

if (className.match(/^M?pfc/)) {

// Check global object cache first, then return that object
//
try {
if (className in global_class_cache) {
return global_class_cache[className];
}
}
catch (e) {
// Probably no global_class_cache yet
global_class_cache = new Object();
}

}

// Not in global object cache, create object
var obj = null;

if (pfcIsWindows()) {
obj = new ActiveXObject("pfc."+className);
}
else {
obj = Components.classes ["@ptc.com/pfc/" + className + ";1"].createInstance();
}

// Return created object
//
if (className.match(/^M?pfc/)) {
global_class_cache[className] = obj;
}

return obj;
}

1 comment:

jr_civetta said...

Thank you you save me lot of process time with this code