views:

193

answers:

1

I just noticed something concerning for-loop performance that seems to fly in the face of the recommendations given by the Google Android team. Look at the following code:

package com.jackcholt;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class Main extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        loopTest();
        finish();
    }

    private void loopTest() {
        final long loopCount = 1228800;
        final int[] image = new int[8 * 320 * 480];
        long start = System.currentTimeMillis();
        for (int i = 0; i < (8 * 320 * 480); i++) {
            image[i] = i;
        }
        for (int i = 0; i < (8 * 320 * 480); i++) {
            image[i] = i;
        }

        Log.i("loopTest", "Elapsed time (recompute loop limit): " + (System.currentTimeMillis() - start));

        start = System.currentTimeMillis();
        for (int i = 0; i < 1228800; i++) {
            image[i] = i;
        }
        for (int i = 0; i < 1228800; i++) {
            image[i] = i;
        }
        Log.i("loopTest", "Elapsed time (literal loop limit): " + (System.currentTimeMillis() - start));

        start = System.currentTimeMillis();
        for (int i = 0; i < loopCount; i++) {
            image[i] = i;
        }
        for (int i = 0; i < loopCount; i++) {
            image[i] = i;
        }
        Log.i("loopTest", "Elapsed time (precompute loop limit): " + (System.currentTimeMillis() - start));
    }
}

When I run this code I get the following output in logcat:

I/loopTest(  726): Elapsed time (recompute loop limit): 759
I/loopTest(  726): Elapsed time (literal loop limit): 755
I/loopTest(  726): Elapsed time (precompute loop limit): 1317

As you can see the code that seems to recompute the loop limit value on every iteration of the loop compares very well to the code that uses a literal value for the loop limit. However, the code that uses a variable which contains the precomputed value for the loop limit is significantly slower than either of the others. I'm not surprised that accessing a variable should be slower that using a literal but why does code that looks like it should be using two multiply instructions on every iteration of the loop so comparable in performance to a literal?

Could it be that because literals are the only thing being multiplied, the Java compiler is optimizing out the multiplication and using a precomputed literal?

+1  A: 

(8 * 320 * 480) is absolutely being optimized to 1228800 as a 'compile-time constant' when the for loop code is constructed.

The key takeaway from the documentation on final is that you don't get the privilige of immutable status and this isn't being optimized during construction of the for loop. Even though it is a local variable you 'could' be modifying it (despite the final keyword) within the for loop so it can't be transformed into a literal.

A final variable can only be assigned once. This assignment does not grant the variable immutable status.

nvuono