Java-Gaming.org    
Featured games (91)
games approved by the League of Dukes
Games in Showcase (581)
games submitted by our members
Games in WIP (500)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  NanoTime for dual-core AMD systems.  (Read 4060 times)
0 Members and 1 Guest are viewing this topic.
Offline brackeen

Junior Member





« Posted 2008-04-09 21:20:29 »

You've probably heard of the issue where System.nanoTime() can return strange results on dual-core AMD systems, because the timer is based off of CPU ticks which can be different on each CPU.

Okay, so I don't have a dual-core AMD system to test, but one person said this code works, so I thought I'd go ahead and share it.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
/**
    The GameTimer is used to workaround the issue on some dual-core systems where timing
    values between different CPUs are out of sync. In this case, the value of System.nanoTime()
    can be different depending on what CPU the code is executed on.
   
    This code attempts to guess what CPU the code is executed on, and adjusts accordingly.
   
    *** For best results, only call nanoTime() once per frame. ***
*/

public class GameTimer {
   
    private static final GameTimer INSTANCE = new GameTimer();
   
    private static final int NUM_TIMERS = 8;
    private static final long MAX_DIFF = 1000000000L; // 1 sec
   private static final long NEVER_USED = -1;
   
    private long[] lastTimeStamps = new long[NUM_TIMERS];
    private long[] timeSinceLastUsed = new long[NUM_TIMERS];
   
    private long virtualNanoTime = 0;
    private int timesInARowNewTimerChosen = 0;
    private long lastDiff = 0;
   
    public static GameTimer getInstance() {
        return INSTANCE;
    }
   
    private GameTimer() {
        for (int i = 0; i < NUM_TIMERS; i++) {
            timeSinceLastUsed[i] = NEVER_USED;
        }
    }
   
    public long nanoTime() {
        long diff;
       
        if (timesInARowNewTimerChosen >= NUM_TIMERS) {
            long nanoTime = System.currentTimeMillis() * 1000000;
            diff = nanoTime - lastTimeStamps[0];
        }
        else {  
            long nanoTime = System.nanoTime();
   
            // Find which timer the nanoTime value came from
           int bestTimer = -1;
            long bestDiff = 0;
            for (int i = 0; i < NUM_TIMERS; i++) {
                if (timeSinceLastUsed[i] != NEVER_USED) {
                    long t = lastTimeStamps[i] + timeSinceLastUsed[i];
                    long timerDiff = nanoTime - t;
                    if (timerDiff > 0 && timerDiff < MAX_DIFF) {
                        if (bestTimer == -1 || timerDiff < bestDiff) {
                            bestTimer = i;
                            bestDiff = timerDiff;
                        }
                    }
                }
            }
           
            // No best timer found
           if (bestTimer == -1) {
                // Use last good diff
               diff = lastDiff;
               
                // Find a new timer
               bestTimer = 0;
                for (int i = 0; i < NUM_TIMERS; i++) {
                    if (timeSinceLastUsed[i] == NEVER_USED) {
                        // This timer never used - use it
                       bestTimer = i;
                        break;
                    }
                    else if (timeSinceLastUsed[i] > timeSinceLastUsed[bestTimer]) {
                        // Least used timer so far, but keep looking
                       bestTimer = i;
                    }
                }
                timesInARowNewTimerChosen++;
            }
            else {
                timesInARowNewTimerChosen = 0;
                diff = nanoTime - lastTimeStamps[bestTimer] - timeSinceLastUsed[bestTimer];
                // Set lastDiff if this same timer used twice in a row
               if (timeSinceLastUsed[bestTimer] == 0) {
                    lastDiff = diff;
                }
            }
           
            lastTimeStamps[bestTimer] = nanoTime;
            timeSinceLastUsed[bestTimer] = 0;
           
            // Increment usage of all other timers
           for (int i = 0; i < NUM_TIMERS; i++) {
                if (i != bestTimer && timeSinceLastUsed[i] != NEVER_USED) {
                    timeSinceLastUsed[i] += diff;
                }
            }
           
            // Check for total failure
           if (timesInARowNewTimerChosen >= NUM_TIMERS) {
                lastTimeStamps[0] = System.currentTimeMillis() * 1000000;
            }
        }
       
        virtualNanoTime += diff;
       
        return virtualNanoTime;
    }
}
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 605
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #1 - Posted 2008-04-09 22:33:07 »

It's kinda sad that when timesInARowNewTimerChosen reaches NUM_TIMERS only once, it will use System.currentTimeMillis() forever. There should be some kind of max-period it is used, after which timesInARowNewTimerChosen is reset to 0

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline brackeen

Junior Member





« Reply #2 - Posted 2008-04-09 23:31:15 »

My thinking is that if it gets to that point, nanoTime is completely unreliable, and currentTimeMillis is used as a fall back. I doubt it will ever get to that point.

But hey, consider the code public domain - feel free to modify it.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline darkprophet

Senior Member




Go Go Gadget Arms


« Reply #3 - Posted 2008-04-10 00:47:22 »

Or you could just install the AMD Dual-Core Optimizer from here...

Quote
The AMD Dual-Core Optimizer can help improve some PC gaming video performance by compensating for those applications that bypass the Windows API for timing by directly using the RDTSC (Read Time Stamp Counter) instruction. Applications that rely on RDTSC do not benefit from the logic in the operating system to properly account for the affect of power management mechanisms on the rate at which a processor core's Time Stamp Counter (TSC) is incremented. The AMD Dual-Core Optimizer helps to correct the resulting video performance effects or other incorrect timing effects that these applications may experience on dual-core processor systems, by periodically adjusting the core time-stamp-counters, so that they are synchronized.

It smoothed alot of the games that I have and a LWJGL app too!

DP Smiley

Friends don't let friends make MMORPGs.

Blog | Volatile-Engine
Offline Markus_Persson

JGO Wizard


Medals: 12
Projects: 19


Mojang Specifications


« Reply #4 - Posted 2008-04-11 08:42:39 »

Or you could just install the AMD Dual-Core Optimizer from here..

Of course, but that's not the point. The end users need to install that as well, which is a lot tricker to achieve.

Play Minecraft!
Offline zammbi

JGO Coder


Medals: 4



« Reply #5 - Posted 2008-12-11 21:24:38 »

I have been thinking to change my timer and use currentTimeMillis() in my game.
Are there still computers being made with the timer issue? About what % of computers would still have this issue?

Current project - Rename and Sort
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

xsi3rr4x (55 views)
2014-04-15 18:08:23

BurntPizza (53 views)
2014-04-15 03:46:01

UprightPath (66 views)
2014-04-14 17:39:50

UprightPath (49 views)
2014-04-14 17:35:47

Porlus (66 views)
2014-04-14 15:48:38

tom_mai78101 (90 views)
2014-04-10 04:04:31

BurntPizza (151 views)
2014-04-08 23:06:04

tom_mai78101 (246 views)
2014-04-05 13:34:39

trollwarrior1 (204 views)
2014-04-04 12:06:45

CJLetsGame (211 views)
2014-04-01 02:16:10
List of Learning Resources
by SHC
2014-04-18 03:17:39

List of Learning Resources
by Longarmx
2014-04-08 03:14:44

Good Examples
by matheus23
2014-04-05 13:51:37

Good Examples
by Grunnt
2014-04-03 15:48:46

Good Examples
by Grunnt
2014-04-03 15:48:37

Good Examples
by matheus23
2014-04-01 18:40:51

Good Examples
by matheus23
2014-04-01 18:40:34

Anonymous/Local/Inner class gotchas
by Roquen
2014-03-11 15:22:30
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!