Open UI: Property Set Methods (Fixing Copy)

Open UI: New Property Set Structure
Open UI: Property Set Methods (Copy, DeepCopy, Clone)
Open UI: Property Set Methods (Why Clone?)

In this article, we will look at the potential speed differences between Property Set methods Copy, DeepCopy and Clone. If we started from a clean Property Set, and used Copy, DeepCopy or Clone, to "copy" a Property Set, they would provide the same output and could be used interchangeably.

Just by looking at the function definition for Clone from the previous article, it is leaner, it doesn’t need to perform a Reset, or test for a null input, because Clone is called on an established Property Set, so in theory it will be the fastest. DeepCopy should perform just as fast as Clone for clean Property Sets.

The test case is to compare the run times for Copy, DeepCopy and Clone, using FireFox, IE and Chrome. The aim isn't to compare which browser is faster, but compare the methods relative to each other on the same browser.

I've beefed up our previous test case to include 4 levels in the hierarchy, and run it with the results pictured below.

var ps=theApplication().NewPropertySet();
var psChild1=theApplication().NewPropertySet();
var psGrandChild1=theApplication().NewPropertySet();
var psGrandChild2=theApplication().NewPropertySet();
var psGrandChild3=theApplication().NewPropertySet();
var psGreatGrandChild1=theApplication().NewPropertySet();
var psChild2=theApplication().NewPropertySet();
ps.SetType("Parent");
ps.SetValue("ParentValue");
ps.SetProperty("Mum","Georgette");
ps.SetProperty("Dad","George");
    psChild1.SetType("Child 1");
    psChild1.SetProperty("Name","Bob");
    psChild1.SetProperty("Gender","Male");
        psGrandChild1.SetType("Grand Child 1");
        psGrandChild1.SetProperty("Name","Bobby's Kid 1");
        psGrandChild1.SetProperty("Gender","Male");
            psGreatGrandChild1.SetType("Great Grand Child 1");
            psGreatGrandChild1.SetProperty("Name","Bobby's Grand Kid 1");
            psGreatGrandChild1.SetProperty("Gender","Male");
        psGrandChild2.SetType("Grand Child 2");
        psGrandChild2.SetProperty("Name","Bobby's Kid 2");
        psGrandChild2.SetProperty("Gender","Male");        
    psChild2.SetType("Child 2");
    psChild2.SetProperty("Name","Jane");
    psChild2.SetProperty("Gender","Female");
        psGrandChild3.SetType("Grand Child 3");
        psGrandChild3.SetProperty("Name","Janes's Kid 2");
        psGrandChild3.SetProperty("Gender","Male");         
psGrandChild1.AddChild(psGreatGrandChild1);
psChild1.AddChild(psGrandChild1);
psChild1.AddChild(psGrandChild2);
psChild2.AddChild(psGrandChild3);
ps.AddChild(psChild1);
ps.AddChild(psChild2);
var psCopy=theApplication().NewPropertySet();
psCopy.Copy(ps);

console.log( "---------------------Copy Method---------------------\n"
+util.PSToStr(psCopy) );
var psClone=ps.Clone();
console.log( "---------------------Clone Method---------------------\n"
+util.PSToStr(psClone) );
var psDeepCopy=theApplication().NewPropertySet();
psDeepCopy.DeepCopy(ps);
console.log( "---------------------DeepCopy Method---------------------\n"
+util.PSToStr(psDeepCopy) );


Results:







The results show something extraordinary with Copy, It looks like the recursion traverses the tree, and only the last Property Set of the children of the Parent nodes are returned.

Copy doesn't perform a full copy as we expected. To verify whether this has been broken in prior versions of Siebel, I ran the same test case in Siebel 8.1.1.5 HI, which does correctly copy the full Property Set, confirming the defect in 8.1.1.10.

To see why this is broken, we need to inspect the definition for Copy again.

function JSSPropertySet_Copy(old) {
var i;
var oldChild;
var name;
if (this.axObj != null) {
        if (old == null)
                return (false);
        this.axObj.Copy(old.axObj); // this should return a value
        return (true);
}
this.Reset();
if (old == null)
        return (false);
name = old.GetType();
if (name != null && name != "")
        this.SetType(name);
value = old.GetValue();
if (value != null && value != "")
        this.SetValue(value);
for (name in old.propArray)
        this.SetPropertyStr(name, old.propArray[name]);
for (i = 0; i < old.childArray.length; i++) {
        oldChild = old.childArray[i];
        if (oldChild == null)
                        break;
        newChild = new JSSPropertySet();
        newChild.Copy(oldChild);
        this.AddChild(newChild);
}
return (true);  
}


The key area to focus on would be the for loop, and the recursion line newChild.Copy(oldChild);.

for (i = 0; i < old.childArray.length; i++) {
        oldChild = old.childArray[i];
        if (oldChild == null)
                        break;
        newChild = new JSSPropertySet();
        newChild.Copy(oldChild);
        this.AddChild(newChild);
}       


An interesting thing to note is that the newChild variable is never declared in this function. Although this is generally considered bad practice, as it causes the variable to be created in Global scope, the Clone method uses this same technique, and it does function correctly.

The above block will loop through all the children of the parent, and perform the recursion at each children. This recursion design should work because, the children and all its children are passed into another Copy method, until all the descendants have been found, and returned back the parent.

The problem here is the use of that undeclared newChild variable, and this line newChild = new JSSPropertySet();.

The newChild Property Set is reset each time it iterates through a new child, therefore wiping out the parent Property Sets. The recursion will eventually finish at the last child, which is not necessarily the deepest.

This diagram shows how this flawed recursion will work.



Points 1,2,3,4 shows the path that the recursion will take. The Property Set at points 1,2 will be clobbered by the statement newChild = new JSSPropertySet();. Only Property Sets 3 and 4 will actually make it out alive, as can be seen in the result above.

Fixing Copy

A simple fix is to declare newChild as a local variable as follows.

for (i = 0; i < old.childArray.length; i++) {
        oldChild = old.childArray[i];
        if (oldChild == null)
                        break;
        var newChild = new JSSPropertySet();
        newChild.Copy(oldChild);
        this.AddChild(newChild);
}       


To test our fix, re-running our first test case again, shows this result.



Copy, DeepCopy, Clone times

After putting that fix in, we can now reliably run the performance timings. Again, this is not a browser test, but a comparison of the speed difference between Copy, DeepCopy and Clone. I’ve had to run a lot less iterations in FireFox, and IE, to ensure the execution didn't time out the browser.



Result: In each graph, the baseline is represented by 0, which is the fastest function. The other columns represents, how slow the respective methods are compared to the baseline function.

Summary

* Clone is faster in Chrome and IE, and only losing to DeepCopy by 3% in FF
* There isn’t much different between DeepCopy and Clone in FF and IE
* Copy is undoubtedly slower, being 22% slower than Clone in IE, 8% slower in FF, and 18% slower in Chrome


The bottom line is that Copy is the slowest, the biggest difference will be seen if you have a massive hierarchy to Copy, or if you have to loop several thousand times, but for once off operations, you may see little practical difference. However, I would take any performance benefit where I can get, it if the effort is minuscule.

The fix we put in for this article is purely for educational purposes. I would recommend you use DeepCopy, and perform a Reset on the property beforehand, and wait until this is fixed officially by Siebel in future releases.

The purpose of Clone

Copy has been broken inadvertently, but Siebel would not design a new method with a different name to replace the broken one. Siebel would also not design a better performing function and give it a different name.

The inherent difference in the design of Copy/DeepCopy Vs Clone should provide a little clue to the purpose of Clone, does anyone want to speculate? The answer deserves an in-depth explanation, as it involves a fundamental misconception in Java Script/Browser Script/eScript.

I've tried hard to separate Copy, DeepCopy and Clone, but the mystery seems to get deeper the more we look. In the last article on these 3 methods I'll provide the definitive meaning of life for Clone.



0 comments:

Post a Comment

Comments are open to all, please make it constructive.