libGDX performance tips, what I have learned so far!
Hey everyone!
Today I wanted to write about a more technical topic – libGDX Performance. I spent the last week in improving the performance and memory usage in Timbertales. Since it is my first libGDX project and even my first openGL project. I am still at the beginning, but I wanted to share my experience so far. The first problem I faced off was: I usually test my developer version on my macbook (compiling is faster than testing on phone/tablet or simulator). On the macbook we have a lot of cpu power and memory. So I often ended up with 60 fps and the usage of memory was also quite ok. But when I test the same version on my kindle fire the fps break down to 20fps and the memory allocation is out of control which ends up in heavy garbage collecting and resulting in a stuttering software.
Tip number 1: Always test on different hardware! Make sure to test on slow hardware
A slow hardware also have the great advantage: You can see improvements immediately. For example I had parsed a JSON string on my macbook, which was quite big. It took 90ms, so I reduced the size of the string and ended up with 79ms for parsing. This is quite good, but it don’t feel very impressive. Then I checked the same JSON string on my kindle with the original length of the String it took 350ms, after my reduction the kindle just took 90ms. As you can see the improvements scale better on slower hardware or is better visible 🙂
Tip number 2: Avoid String concatenation in the render calls
I used a lot of String concatenation in my render calls first for example
int score = 3;
font.draw(batch, "" + score, x, y);
This one seems pretty simple and shouldn’t do that much. This was at least what I thought, but this one always creates a new String, every single time its called, which is normally 60 times in a second, since we want to achieve 60fps. So what it does is: Allocate memory for the new String Object with every call, if you have multiple calls like that and just a little bit of free memory it will fill up fast and the garbage collector has to do his work. This will not leak memory or something, but it will call the garbage collector very often, which can result in stuttering (The garbage collector is not running for free). To avoid that problem make sure to set up your strings, objects outside the render calls.
font.draw(batch, scoreString, x, y);
Tip number 3: Avoid to create new instances of any objects
It is related to tip 2. You should never create an instance of a new object inside the render call, because it will create a new instance every draw call. As I said before these are 60 calls / second and java will always allocate new memory for the new instance. So avoid stuff like that:
pubic void draw(SpriteBatch batch) {
Effect healEffect = new Effect(); // this one should not be instantiated inside the draw call
healEffect.draw();
}
Tip number 4: Assets and other resources
As I said I am new to the whole openGL and libGDX stuff, so maybe the tips I write are pretty obvious for other people. I often made the mistake to get resources or translations out of my asset manager while rendering, this resulted often in terrible much memory allocations.
public void draw(SpriteBatch batch) {
batch.draw(Assets.instance.getTextures().unitTexture, x, y);
font.draw(batch, Assets.instance.getBundle().format("translation", "text to insert"), x, y);
}
The texture call isn’t that bad at all, but it is always better to have a local reference. The second call with the bundle was actual a very big problem. As the string concatenation problem, the format always creates an String Objects in background which also allocated a lot of memory. To avoid these problems I use the approach to create my Strings before rendering and save the assets as local references:
public void draw(SpriteBatch batch) {
batch.draw(this.unitTexture, x, y);
font.draw(batch, this.translationString, x, y);
}
Tip number 5: Do manual iteration over Array lists
This one was mysterious for me 🙂 I read through a page for java performance and improvement, because I always had the feeling that there was a lot of memory allocation ongoing when I used Array lists and I couldn’t understand why? So on this page I just read that the standard iterator for array lists is creating allocations, where the manual way doesn’t so what I did was to change every “for” loop in my code inside the render calls:
// old usage with lot of allocation
for(Object object: objects) {
}
// new manual way with no allocations
int length = objects.size();
for(int i = 0; i < length; i++) {
Object object = objects.get(i);
}
// UPDATE you can also use libGDX Array
So I had to write a little more code, but the result was – no more allocations inside my draw calls with array lists.
Update:
I hope this post can help people, who also have some issues with allocations or performance. I guess for more experienced people these tips are more obvious, but maybe you can give us some more tips on this topic? Feel free to comment or discuss with me.
Make sure to check out my games written in libGDX: FlatFatCat and Timbertales
Recent Comments