Tad Frysinger (25th June 2005) wrote:
I've noticed that the Java 5.0 native nanosecond timer, as well as the J3D nanosecond timer suffer strange behavior when run on Intel chips that employ the so-called "hyper-threading" to simulate multiple processors. Apparently, however many threads are so defined causes a division among them for timing cycles.
What this means on my Dell Inspiron 5150, for example, is that when I run your timing tests (a slightly modified TimerRes.java), I get error rates approaching or matching 100%. I ask for 2000 milliseconds, and I get 1000 milliseconds elapsed and then the Thread.sleep() call finishes. Presumably, the other 1000 milliseconds is being handled by the "other processor", or some such:
Java 3D Timer Resolution: 1 nsecs System Time resolution: 15750 microsecs Nano Time resolution: 805 ns System.currentTimeMillis() ---------------------------- Sleep request: 2000 ms slept: 2000 ms: 2000 ms err: 0 % Sleep request: 1000 ms slept: 1000 ms: 1000 ms err: 0 % Sleep request: 500 ms slept: 500 ms: 500 ms err: 0 % Sleep request: 200 ms slept: 204 ms: 204 ms err: 0 % Sleep request: 100 ms slept: 93 ms: 93 ms err: 0 % Java3DTimer.getValue() ------------------------- Slept: 2000 ms J3D: 997807621(ns) 997.81 ms err: 100.44 % Slept: 1000 ms J3D: 499086826(ns) 499.09 ms err: 100.37 % Slept: 500 ms J3D: 249593368(ns) 249.59 ms err: 100.33 % Slept: 200 ms J3D: 99904405(ns) 99.9 ms err: 100.19 % Slept: 100 ms J3D: 50107689(ns) 50.11 ms err: 99.57 % Slept: 50 ms J3D: 25194949(ns) 25.19 ms err: 98.45 % Slept: 20 ms J3D: 10066692(ns) 10.07 ms err: 98.67 % Slept: 10 ms J3D: 5227398(ns) 5.23 ms err: 91.3 % Slept: 5 ms J3D: 2669354(ns) 2.67 ms err: 87.31 % Slept: 1 ms J3D: 639145(ns) 0.64 ms err: 56.46 % System.nanoTime() -------------------- Slept: 2000 ms Sys: 998102301(ns) 998 ms err: 100.4 % Slept: 1000 ms Sys: 498812166(ns) 498 ms err: 100.8 % Slept: 500 ms Sys: 249178703(ns) 249 ms err: 100.8 % Slept: 200 ms Sys: 99913312(ns) 99 ms err: 102.02 % Slept: 100 ms Sys: 50108869(ns) 50 ms err: 100 % Slept: 50 ms Sys: 25217756(ns) 25 ms err: 100 % Slept: 20 ms Sys: 10089247(ns) 10 ms err: 100 % Slept: 10 ms Sys: 5211676(ns) 5 ms err: 100 % Slept: 5 ms Sys: 2761904(ns) 2 ms err: 150 % Slept: 1 ms Sys: 810654(ns) 0 ms err: ? %
My response (28th June 2005):
You didn't mention which version of Java 3D you were using. A timer bug related to hyperthreading was fixed in Java 3D 1.3.2, as described in Sun's bug database and Java 3D's issue tracker. I haven't had the opportunity to test it myself.
This bug was caused by the timer code only using the lower 32 bits of the result from the QueryPerformanceCounter() function in the underlying Windows API, a problem described in Microsoft's Knowledge Base Article 327809. The symptom of this bug was getting back a 0 for the time, which isn't your problem.
QueryPerformanceCounter() gets its value in different ways depending on the underlying hardware. On a uniprocessor, it's usually derived from the countdown value of the motherboard IRQ0 timer. On a multiprocessor or hyperthreaded machine, QueryPerformanceCounter() uses a RDTSC (Read Time Stamp Counter).
One issue with multiprocessor machines, is that the 'affinity' mask must be set to ensure that the same CPU clock is always used. Affinity should be irrelevant for a single processor using hyperthreading.
Another problem is that RDTSC may produce erroneous values on some mobile platforms because it assumes a fixed clock speed. In fact, mobile platforms (such as notebooks) may throttle back the CPU at certain times, thereby breaking RDTSC's assumption.
Yet another problem is that QueryPerformanceCounter() may unexpectedly leap forward several seconds from time to time on some processors. See Microsoft's Knowledge Base Article 274323 for details.
An informative thread about these timer problems (as they appeared in LWJGL) can be found at the Java Games Forums. Here's another in the comp.sys.intel newsgroup.
The simplest thing seems to be to switch off hyperthreading. You could also try changing to System.currentTimeMillis().
Joe Bowbeer (19th July 2005) wrote:
I'd like to mention using interrupt(), either alone or in combination with a boolean flag.
public void run() { while (!Thread.interrupted()) { // keep running } }
You could also use !thread.isInterrupted() which leaves the interrupt bit set.
The approach is explained in How To Stop A Thread or a Task.
My response (20th July 2005):
interrupt() is useful when the thread is sleeping or waiting, and you have to get out, at any cost. I'm not so sure if interrupt() is such a good idea in my animation loop which only sleeps for a very short time. If the code isn't going to wait or sleep forever, then perhaps it's easier/simpler to accept a delay before the thread stops, and stick with a running boolean as the sole stopping mechanism.
The web page also discusses the use of the InterruptibleChannel interface. Classes implementing that interface can interrupt blocking I/O. Once again, I'm not sure how useful this coding style is for my rendering loop, but it's certainly a technique to remember.
Alan Stange (24th January 2006) wrote:
I suggest modifying the paintScreen() method on p.22. Currently, it's basically:
private void paintScreen() { Graphics g; try { g = getGraphics(); // do something g.dispose(); } catch (Exception e) { // print an error } }
It would be safer to move the dispose() call to a finally block so it can still be called if there's an exception:
private void paintScreen() { Graphics g; try { g = getGraphics(); // do something } catch (Exception e) { // print an error } finally { if (g != null) g.dispose(); } }
My response (1st February 2006; updated 22nd February):
Good idea. It would be safer.
But the first line should be Graphics g = null;, as
pointed out by Keith Kee.
Blaine Simpson (15th May 2006) wrote:
I realize that you use your own double buffering so it can be combined with active rendering. But then you might as well turn off Swing's default double-buffering for the JPanel.
My response (15th May):
You're right. Add the line:
setDoubleBuffered(false);
to the GamePanel constructor.
In fact, I do this in several of the later examples. See the BugPanel() constructor in chapter 11, p.274, and the JackPanel() constructor in chapter 12, p.304.
Blaine Simpson (15-16th May 2006) wrote:
Your pause behaviour is inefficient: although a pause causes game updates to stop, rendering is still being carried out, on a non-changing buffer, 80 times/second.
The justification you supply at the top of p.39 is an argument for calling paintScreen(), but why bother calling gameRender() as well?
My response (16th May):
You want to put an if-test similar to the one in gameUpdate():
if (!isPaused && !gameOver)around the code inside gameRender()? It's a great idea.
One issue is that although the 'game' is paused, you may still want the screen to be redrawn. For example, in a network game even when your client appl. is paused, it may still want to keep checking and reporting on the network connection, updating a list of messages from other players, etc.
The Worm code illustrates this idea since it keeps updating the FPS/UPS info on screen even while the appl. is paused.
Another point may be to have different behaviours inside gameRender() depending on whether the application is minimized (so not visible) or deactivated. This subdivision of the meaning of 'pause' could also be used to skip the call to paintScreen() when the window was minimized.
Ed Jensen (12th Dec 2006) wrote:
Your animation loop calls Thread.yield() if the thread hasn't slept for a while, to give other threads (such as the event dispatcher) a chance to progress. The 'lack' of sleep is measured by the noDelay counter.
In that case, when a sleep() call does succeed, then you should reset noDelays to 0, since the other threads will have had a chance to run.
Here's the modified part of the animation loop in run():
if (sleepTime > 0) { // some time left in this cycle try { Thread.sleep(sleepTime/1000000L); // nano -> ms noDelays = 0; // reset noDelays when sleep occurs } catch(InterruptedException ex){} overSleepTime = (J3DTimer.getValue() - afterTime) - sleepTime; } else { // sleepTime <= 0; the frame took longer than the period excess -= sleepTime; // store excess time value overSleepTime = 0L; if (++noDelays >= NO_DELAYS_PER_YIELD) { Thread.yield(); // give another thread a chance to run noDelays = 0; } }
My response (17th Dec):
A very nice improvement.
Henry Buckley (2nd September 2007) wrote:
I've a comment on the code example on page 37, illustrating pausing and resuming using wait().
On resuming from a pause, wouldn't the timeDiff variable include the time paused, which would in turn lead to gameUpdate() being called multiple times without rendering -- i.e. the game state wouldn't actually be paused at all?
I realise that your preferred solution on page 38 doesn't have this problem, as in this case the timeDiff variable gets continually refreshed during the pause period.
My response (3rd Sept.):
You're right. I guess that's another reason for not using the wait/notify approach.