tags:

views:

418

answers:

6

I wrote a simple applet to have as a base fr making games, and it's already using up >50% of my CPU. I'm on a 3ghz P4 with 1.5gb ram, so I know it shouldn't take up THAT much.

import java.awt.*;
import java.applet.*;

public class applettest extends Applet implements Runnable {

    long lastFrame;

    public void init() { 
     (new Thread(this)).start();
    }

    public void paint(Graphics g) {
     g.drawString("Welcome to Java!!", 50, 60 ); 
    }

    public void run() { 
     while(true) {
      // code here
      repaint();

      try{
       // wait 16 milliseconds to cap frame rate to 60 fps
          while (System.nanoTime() < lastFrame + 160000000)      {
           Thread.yield();
          }

          lastFrame = System.nanoTime();
      }

      catch(Exception e){}
     }
    }
}
+8  A: 

Try replacing your busy wait with

lastFrame = System.currentTimeMillis();

while(true) 
{
   repaint();

   long msSleep = 16 - (System.currentTimeMillis() - lastFrame);
   lastFrame = System.currentTimeMillis();

   if(nsToSleep > 0)
   {
      Thread.sleep(msSleep);
   }
   else
   {
      Thread.yield();  // Only necessary if you want to guarantee that
                       // the thread yields the CPU between every frame
   }
}

This will guarantee that the time between frames is at least 16ms. If your render time is less than 16ms, and there aren't any other threads hogging the CPU, it will run at 60fps.

Your original solution will enforce the 16ms minimum, but it has to keep polling the system time over and over (which uses the CPU) until the necessary amount of time has passed.

Notes:

Daniel LeCheminant
That worked. Thanks. :)
William
So, the solution was to "sleep" + "yield"?
OscarRyz
Just to add, repaint() is asynchronous so the elapsed time between the 2 time points will be ~0. To measure the time used for painting you'd need to perform some kind of active rendering.
Pool
Actually, there's no real guarantee that the repaint will occur once per loop, multiple repaint calls can be combined and repainting occurs when possible.
Pool
A: 

You are probably seeing only 50% due to java not using the second core of your CPU.

Your code will do the addition and comparison as fast as the CPU can do them.

// wait 16 milliseconds to cap frame rate to 60 fps
while (System.nanoTime() < lastFrame + 16000000)        {
  Thread.yield();
}

Use Thread.sleep(16) instead of the tight while loop with lots of math.

Chris Nava
He's trying to ensure that the time between frames at *most* 16ms; Thread.sleep(16) will guarantee that the time between frames is at *least* 16ms.
Daniel LeCheminant
This should note be the accepted answer.
jjnguy
Agree with Daniel and jjnguy -- This answer is not correct.
William Brendel
True. This answer explains why the CPU utilisation is so high and suggests a simple fix. In order to have a better average framerate he could do a sleep(4) inside the loop instead of yeald() and adjust the math to get an average of 16hz.
Chris Nava
I like the sleep inside his original loop; it's certainly a simple fix :]
Daniel LeCheminant
what should the new math be for the sleep in the loop?
William
I would divide the remaining time to wait in half or 1/3, sleep that long and divide the true remaining time in half or 1/3... etc. That way you sleep a bunch when it's unlikely you'll overshoot and less when you may.
Chris Nava
+2  A: 

You're doing a busy-wait until the time hits a certain threshold. However, yielding does not mean that your thread stops running if no other thread wants the time...

If you want to wait and cut down the CPU use, consider Thread.sleep

Uri
+1  A: 

Note that 1600000 ns = 1.6 ms, so you are probably not sleeping for as long as you expect.

zdan
A: 

The difference between Thread.sleep and a busy wait is that when you put the thread to sleep you are telling the computer that you don't need to do anything for x seconds and the pc will let some other process use that time.

However, with a busy wait you are telling your machine to constantly run the loop code, even if it's not really accomplishing anything.

ZainR
+5  A: 

Sorry that this post is more of the "and for further information..." type than a direct answer, which I think has now been given-- I just thought it was helpful not to get things lost inside comments.

This thread seems to show a lot of (admittedly typical) misunderstanding about what methods like Thread.sleep() and Thread.yield() actually do. I've previously written some material that people may find interesting where I've tried to clear some of these matters up: Thread.sleep() (including a graph of behaviour under different levels of load), Thread.yield() and-- relatedly though it wasn't mentioned here, people might be interested in my look at thread priorities.

Incidentally, there is generally no benefit in using System.nanoTime() in this case: you'll just get a time rounded to the nearest millisecond in any case. I'd save yourslef the awkward calculations and potential overhead of retrieving the nanosecond clock time (though the latter is not so bad either) and just use good ole' traditional System.currentTimeMillis(). If your thread oversleeps, just compensate next time by sleeping for less.

Neil Coffey
+1 for good info regarding System.nanotime
Daniel LeCheminant