Tuesday, 10 January 2012

Accessors are slow

This is the most important performance optimisation I learned writing ActionScript. It provides a significant performance boost, and in class-based code arises more often than almost all other optimisations. It also flies in the face of what I learned as a C++ programmer.


In C++ accessors are key to good programming practice. Their main use is to hide the implementation from the interface of a class, so the class can be updated without code using it having to be re-written. They make it easy to add code to record accesses for e.g. ref-counting or debugging purposes. Making members private and providing accessors for them documents the code as it indicates which members are safe to access directly which are not because they e.g. have side effects.


This works well in C++ as such accessors can be eliminated by inlining, where the function call is replaced by code accessing the member directly. This is done by the compiler to trade code size for performance, which means there's a limit to how much this is done in case the program grows to big. But for small accessors it almost always happens unless explicitly disabled for debugging.


But in ActionScript it doesn't happen. Even the simplest functions are still called, at significant overhead. This means accessors should only be used where performance doesn't matter. In practice though as the performance difference is so great I prefer to avoid accessors altogether.


The following code for example has two functions, identical except one accesses members directly, the other uses accessors. The second function takes over four times as long as the first. And in real-world code the difference can be even greater, if data is organised into classes so all data is accessed as members.



function Test1():void {
    var iTrial:int, iSum:Number, v:Vec2D;
    iSum = 0;
    for (iTrial = 0; iTrial < kNumTrials; iTrial++) {
        v = aData[iTrial];
        iSum += v.iX + v.iY;
        iSum += v.iX + v.iY;
        iSum += v.iX + v.iY;
        iSum += v.iX + v.iY;
    }
}

function Test2():void {
    var iTrial:int, iSum:Number, v:Vec2D;
    iSum = 0;
    for (iTrial = 0; iTrial < kNumTrials; iTrial++) {
        v = aData[iTrial];
        iSum += v.X() + v.Y();
        iSum += v.X() + v.Y();
        iSum += v.X() + v.Y();
        iSum += v.X() + v.Y();
    }
}






No comments:

Post a Comment