0

EDIT: so, this happens only on android, on desktop results are pretty much the same, but on android ugly code is 10x faster. Tested on android 4.4 (samsung galaxy s4), android 8 (nexus 6p), android emulator on Mac.

After refactoring code of my android program I noticed, that method invocation is very performance costly. Lets say, I have a class

public class Chunk {
private byte[] chunkArray;
private ChunkGetter chunkGetter;

public Chunk() {
    chunkArray = new byte[65536];
    chunkGetter = new ChunkGetter();
}

public byte getByteFromArray(int x, int y, int z) {
    return chunkGetter.getBlockId(x, y, z, chunkArray);
}

public byte[] getChunkArray() {
    return chunkArray;
    }
}

and a getter to get data from a chunk array:

public ChunkGetter() {

}

public byte getBlockId(int x, int y, int z, byte[] blocksByteArray) {
    return blocksByteArray[getCoordinateOffset(x, y, z)];
}

public static int getCoordinateOffset(int x, int y, int z) {
    return x * 256 * 16 + z * 256 + y;
}

So, a simple getting test gave me these results:

private void runTest() {
    Chunk chunk = new Chunk();
    long start = System.nanoTime();
    for (int x = 0; x < 16; x++) {
        for (int z = 0; z < 16; z++) {
            for (int y = 0; y < 256; y++) {
                byte id = chunk.getByteFromArray(x, y, z);
            }
        }
    }
    LOG("test took: " + (System.nanoTime() - start) / 1000000 + " ms");
}
first call: test took: 19 ms
second call: test took: 16 ms
third call: test took: 17 ms

But if I get data directly from the array - it is 20 times faster:

private void runTest() {
    Chunk chunk = new Chunk();
    byte[] chunkArray = chunk.getChunkArray();
    long start = System.nanoTime();
    for (int x = 0; x < 16; x++) {
        for (int z = 0; z < 16; z++) {
            for (int y = 0; y < 256; y++) {
                byte id = chunkArray[x * 256 * 16 + z * 256 + y];
            }
        }
    }
    LOG("test took: " + (System.nanoTime() - start) / 1000000 + " ms");
}
first call: test took: 1 ms
second call: test took: 1 ms
third call: test took: 1 ms

This code is not readable and not flexible but when using it, my program runs init method in 1.5 sec and when using methods - it runs in 9 sec! How can I achieve good performance without ugly copy-pasting?

  • 3
    The usual: https://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java – GhostCat Sep 07 '17 at 11:28
  • Ok, time to learn how to do benchmarking :) But even without benchmarks I can see a result almost immediately in the second case, while in the first (where I use methods) I need to wait. – user3470643 Sep 07 '17 at 11:53

1 Answers1

0

The Android virtual machine seems to lack some of the optimizations of the desktop JRE's HotSpot engine, maybe the auto-inlining of calls. If that's true, you have to reduce the number of method calls.

Some ideas:

  • Inline the getCoordinateOffset() and getBlockId() methods into Chunk.getByteFromArray() - from your code snippet, I don't see a reason for having a ChunkGetter class. In the outer layer, you'll still have the x/y/z abstraction, and it gets "ugly" only inside the implementing code of getByteFromArray()

  • Why do you represent a logically 3-dimensional array as a linear array, thus making the strange index computation necessary? Directly using a three-dimensional array eliminates the need for the special getters, and might be quite fast.

  • Your nested loops effectively traverse your linearized array sequentially. Instead, you could do a single loop for (int i=0; i<chunkArray.length; i++). No x/y/z abstraction, but probably even faster than your faster version.

Maybe some of these hints might help - only benchmarking will tell, and you decide on the tradeoff between (your view of) readability and speed.

Ralf Kleberhoff
  • 6,990
  • 1
  • 13
  • 7
  • Thank you for the answer! I have simplified my code to better describe my question. In real program I have few implementation of Chunk and ChunkGetter for different purposes. I ended up with inlining methods in the places where performance is critically important. – user3470643 Sep 07 '17 at 18:49