1

I'm currently developing a small desktop java game using the libgdx framework. Everything is woking fine, except some really strange CPU usage behaviour.

When running just the framework background with an empty rendering loop i get about 28% CPU usage. This seems fine considering lbgdx is also doing some management and the render method is called as fast as possible.

But here things start to get crazy ...

When drawing and updating about 200 objects, the CPU usage jumps up to about 55%. Libgdx however tells me it uses only a single open gl draw call. CPU overhead should be really low then, shouldn't it? Also the CPU usage is not caused by the update loop. I already tried removing it, but the usage is still up to about 50%.

But it gets even worse ...

I dicovered by accident that when the program has to handle a lot of objects (around 50000) the usage drops to about 30%.

If i deallocate the massive amount of objects then and go back to my 200 objects the CPU usage drops even further to ~20%.

If i repeate this process the usage will eventually drop to 3%. Yes 3%. And it'll stay at 3%. (As long as I'm not allocating any other objects. The usage will increase then, of course)

To sum it up: I'm rendering the exact same scene. At first with 55% CPU usage. And after allocating and deallocating a massive amount of objects with 3% CPU usage.

How could this possibly happen?

At first I figured that lidgdx is using a batch system, that will create batches of vertices, color information and texture coordinates to minimize the amount of draw calls. If I'll set the batch size to 2500 sprites per batch, the CPU usage will drop to 30% - 40%. However, the strange allocating and deallocating effect is still happening.

I'll also give you my update, draw, render and clear methods, if it helps.

Render method:

@Override
public void render()
{   
    //the basic game loop components
    //update all and draw everything
    update();
    draw();

    //Entity.alive refers to an ArrayList containing
    //all objects that'll be updated and draw in the
    //render loop
    System.out.println("ENTITY COUNT " + Entity.alive.size());

    //and remove all the dead bodies
    //from the carpet ... you know
    //i can't stand blood ...
    //and make some new ones, too 
    clear();

    while((System.nanoTime() / 1000000) - time_last < 16)
    {
        //nothing to do here ... (imagine meme)

        try
        {
            Thread.sleep(0, 1000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }

    time_last = System.nanoTime() / 1000000;
}

Update method:

public void update()
{   
    //update all the entities
    //in the "alive" ArrayList
    for(Entity e: Entity.alive) e.update();

    //add all entities declared
    //dead to the dead list
    for(Entity e: Entity.alive)
    {
        //not dead - nothing to do here
        if(e.getState() == true) continue;

        //else add to dead list!
        Entity.dead.add(e);
    }
}

Draw method:

public void draw()
{   
    //clear the screen
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);

    //update the view
    Draw.updateCamera();

    //start a batch section
    //all further calls will
    //be packed an sent to
    //gpu in one flush
    Draw.startBatch();

    //draw all living entities
    for(Entity e: Entity.alive) e.draw();

    //draw all the level
    //geometry and sprites
    Level.draw();       

    //ending the sprite batch
    Draw.endBatch();
}

Clear method:

public void clear()
{
    //remove all dead objects
    for(Entity e: Entity.dead)
    {           
        Entity.alive.remove(e);
    }

    //clear the array of the dead
    Entity.dead.clear();

    //add all the newly created
    //objects to the alive list
    for(Entity e: Entity.born)
    {
        Entity.alive.add(e);
    }

    //clear the creation list
    Entity.born.clear();
}

Im developing the program in eclipse 4.4.1 using java 1.8.0_31-b13. The operating system is Windows 8.1.

genpfault
  • 51,148
  • 11
  • 85
  • 139
Excliff
  • 21
  • 3
  • How are you measuring CPU usage? – P.T. Mar 27 '15 at 18:32
  • 1
    Does CPU usage correlate with your framerate? – Mike Tunnicliffe Mar 27 '15 at 18:35
  • Whoever flagged as *This code is not working* needs to re-read the flag description, please. This is a perfectly valid question, albeit long and a little wordy. – Qix - MONICA WAS MISTREATED Mar 27 '15 at 20:10
  • Also to OP: this is completely a comment of coding style, but you overcomment things a bit. `Entity.dead.clear()` is pretty straightforward, and it doesn't really warrant a comment. It adds a lot of fuzz that otherwise should be used for comments explaining parts that aren't as trivial. Just a thought :) – Qix - MONICA WAS MISTREATED Mar 27 '15 at 20:11
  • Thanks for the defense! :) I knew a got a little wordy, but it was difficult to describe the problem in very few words. Concerning the commenting: You are absolutely right. The tendency for overcommenting code is just a habit I picked up in university. Because a lot of diffentent people will read your code and grades sometimes depend on those guys understanding the program, i tend to comment every single bit just for safety. – Excliff Mar 28 '15 at 11:38
  • About CPU usage. I use the windows task manager to measure it. I don't know if this is a unreliable source or if there is a better way to do this. But if there is, i'd really love to know. ;) – Excliff Mar 28 '15 at 11:41
  • Also the CPU usage does not correlate with the framerate. At least the FPSlogger class of LibGdx tells me so. The program is running on 60 FPS with 55% CPU usage and on 60 FPS with 3% CPU usage. – Excliff Mar 28 '15 at 11:44

3 Answers3

2

your render() method is a bit weird. I don't know whether this contributes to the crazy cpu timings the following code is unnecessary because LibGDX itself handles the 16.6ms wait time (for 60Hz) itself. It might make some troubles if your timer doesn't match LibGDX timer. Therefore you might wait too long and thus reducing cpu load significantly. When removing the Entities the timers will stay out of sync -> reduced cpu usage.

while((System.nanoTime() / 1000000) - time_last < 16)
{
    //nothing to do here ... (imagine meme)

    try
    {
        Thread.sleep(0, 1000);
    }
    catch (InterruptedException e)
    {
        e.printStackTrace();
    }
}
laubed
  • 248
  • 1
  • 9
  • At first: Thanks for your answer! :) You're right about the timing that LibGdx does in the background. So my timing code is redundant. However when I'm removing the code block the usage drop is still there. So this seems not to be the main issue. But i could imagine that the interal libgdx timer somehow gets confused by the massive object creation. – Excliff Mar 28 '15 at 11:52
0

Ok, so after a lot of reading and some experiments i figured it out!

In the DesktopLauncher class that libGDX is using, the programmer is asked to create a config object for the lwjgl init process. Something like this:

public static void main(String[] args)
{
    LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();

    config.width = 1920;
    config.height = 1080;
    config.fullscreen = true;
    config.foregroundFPS = 0;

    new LwjglApplication(new Controller(), config);
}

It turns out the last line config.foregroundFPS = 0; is crucial. This disables all attempts of the framework to set the thread to sleep whenever possible.

I strongly suspect this sleep functionality to be resposible for the strange CPU usage. After disabling this the usage behaves normally now.

Still, thanks a lot for your support! :)

Excliff
  • 21
  • 3
0

As I found, out this issue related to vSync and some specific (nVidia?) drivers. You may use following code to prevent high CPU usage:

LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.vSyncEnabled = false;

Any FPS except 0 and 60 works as well:

LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.backgroundFPS = 59;
config.foregroundFPS = 59;
Pavel
  • 2,557
  • 1
  • 23
  • 19