So i'm trying to implement a sort of day-night cycle in my game, and I'm using System.nanotime() to get the approximate time passed between frames, the problem is that it sometimes jumps huge amounts of time
Using lwjgl, and calling Timer.update() before swapBuffers with vsync enabled should be around 16.6ms increase to the current time each loop shouldnt it? Yet it can be much much higher than that with no actual slowdown for rendering
Here's the code : Time class
public class Time
{
public static final long SECOND = 1000000000L;
private static long lastTime;
public static long getTime()
{
return System.nanoTime();
}
public static double getDelta()
{
return (double)(getTime() - lastTime) / SECOND;
}
public static void update()
{
Time.lastTime = Time.getTime();
}
}
Update method
while ( !glfwWindowShouldClose(window) )
{
input();
update();
render();
}
public void update()
{
//System.out.println("Time since last update " + Time.getDelta());
Time.update();
}
And where i'm using the delta time :
if ((timeOfDay + Time.getDelta()) < timeDayTotal)
timeOfDay += Time.getDelta();
else
timeOfDay += Time.getDelta() - timeDayTotal;
System.out.println("Time of day " + timeOfDay);
Ignoring the fact that the precision seems to be waaaay off for now, here's some sample output
Time of day 0.0077873133
Time of day 0.0077988105
Time of day 0.0078120963
Time of day 0.007860638
Time of day 0.015185255
Time of day 0.01879608
Time of day 0.01880809
Time of day 0.018820863
Time of day 0.018835938
Time of day 0.018851267
It seems to mostly increment the correct amount (by a factor of 10^-4, but close enough, thats not the problem), but then it has these massive jumps up that I can't explain
So finally, a) whats the problem with system.nanoTime and b) is there a fix or viable replacement?
Edit : Switched to currentTimeMillis(), the precision is gone which is no big deal, but the jumps are still there
Time of day 0.03
Time of day 0.03
Time of day 0.03
Time of day 0.03
Time of day 0.06
Time of day 0.06
Time of day 0.06
Time of day 0.06
In general, do not use System.nanoTime() in any program which you don’t plan to strongly control how it is run, and the environment it is run in.
..
The problem lies in the RDTSC instruction which retrieves the number of CPU ticks since the CPU started. On multi-core systems, each core will have its own tick count, and they will not match, so every time your process switches CPUs, you get a different measurement. The issue is compounded by the fact that some power management systems actually alter the CPU’s frequency to save power, which breaks the functionality even on single core, single CPU systems.
From here
From the code you posted, it looks like you update() the Time on each frame before rendering, so then when you use getDelta() you only measure the time it took to get there, rather than the whole frame time?
I think it should look more like this:
public class Time
{
public static final long SECOND = 1000000000L;
public static final double timeDayTotal = 100.0; // ?
private static final long start = System.nanoTime();
private static double timeOfDay;
public static void update() {
long now = System.nanoTime();
timeOfDay = (now - start) / (double)SECOND % timeDayTotal;
}
public static double getTimeOfDay()
{
return timeOfDay;
}
}
Related
I'm creating a simple simulation of gas station as homework. The total duration of the simulation is the week. Filling cars is approximately 3 minutes depending on the type of fuel. Cars may be collected in a queue. Now the question. I know how to implement these methods, but have no idea how to simulate a period of time without methods like a Thread.sleep().
P.S. I'm using JavaFX framework for this task. Cars are represented as javafx.scene.shape.Rectangle and their movements through Tranlsate methods. Dispensers too.
The Thread.sleep() method accepts a millisecond value. Basically, you can run an update and then calculate how long you need to sleep until the next update.
You can measure real time elapsed with System.nanoTime(). Make sure your class implements Runnable. Inside the run method, stick a while loop which contains an update() method to update the cars. Get nano time at the start and end of the loop, subtracting the two which gives you elapsed time. Subtract the elapsed time from the time you want each update to take, then sleep the thread. I think that is really all you need.
Here is the code:
public void run() {
int updatesPerSecond = 5;
/* The target time is the time each update should take.
* You want the target time to be in milliseconds.
* so 5 updates a second is 1000/5 milliseconds. */
int targetTime = 1000 / updatesPerSecond;
long currentTime;
long lastTime = System.nanoTime();
long elapsedTime;
long sleepTime;
while (running) {
// get current time (in nanoseconds)
currentTime = System.nanoTime();
// get time elapsed since last update
elapsedTime = currentTime - lastTime;
lastTime = currentTime;
// run your update
update();
// compute the thread sleep time in milliseconds.
// elapsed time is converted to milliseconds.
sleepTime = targetTime - (elapsedTime / 1000000000);
// don't let sleepTime drop below 0
if (sleepTime < 0) {
sleepTime = 1;
}
// attempt to sleep
try {
Thread.sleep(sleepTime);
} catch(Exception e) {
e.printStackTrace();
}
}
}
Within JavaFX framework,
you can simply use PauseTransition to simulate periodic time elapsing. On end of every period update scene graph elements' states. If you are going to change some properties of some node and do various animations you may utilize other types of Transitions. For more fine grained control you can use Timeline with its KeyFrames.
I have code which checks if the given "A" time (in milliseconds) is in the given "b" time period.
private static boolean isInTimeInterval(long time, int timePeriod) {
long curTime = Calendar.getInstance().getTimeInMillis();
// time period is in hours, 1 hour is 3600000 ms;
long startTime = curTime - timePeriod * 3600000;
if (time >= startTime && time < curTime){
return true;
}
return false;
}
I take the time from a file and parse it into a long like this:
(Long.parseLong(array[2]))
But it doesn't work correctly, what is wrong ?
To simplify things, I would suggest that you first subtract the start time from the end time, check to see if that is positive and then decide if the remaining milliseconds is smaller than the requested time period.
long difference = Calendar.getInstance().getTimeInMillis() - time;
long timeRange = timePeriod * 3600000;
return (0 <= difference && differance <= timeRange);
It makes the code slightly smaller in lines, but more importantly, it simplifies the math to where you know the code isn't the problem.
As far as the errors you are likely encountering, I'd look to your
Long.parseLong(array[2])
As that is likely grabbing the input in a manner you aren't expecting. For starters, I'd put in some logging or at least one-time println debugging statements to verify the input times are what I thought they were.
So I have this piece of code, setting dt in a game loop (clock is of type Clock):
// set delta time
float currentTime = clock.getElapsedTime().asSeconds();
float dt = currentTime - lastTime;
// ...
lastTime = currentTime;
However, when the game is paused, the clock still runs. So as the game is paused, dt becomes large. How would I avoid this?
A possible, basilar approach could be something like the following one (pseudocode):
var elapsed = current - previous;
if(elapsed > clampOverElapsed) {
elapsed = clampOverElapsed;
}
Where clampOverElapsed is set somewhere to a reasonable value, as an example 25 ms.
This way, by having that control within the loop, you should nicely handle pauses as well as unseasonably longer iterations, without caring about what's the source of your big elapsed value, so that you have not to explicit
I am trying to build a map with time starting from 06.00 to 23:59 as keys and I will keep track of a number for each time as value.I need the time to stored HH.mm as format and I am planning to build the map using a loop by incrementing one minute and run the following code inside the loop.The problem here is since I have to set the format as HH.MM strictly I have to get the input as String and format it and then parse it back as double which affects the perfomance.Is there a global setting to change so that whatever double number I choose in this particular class should be of the format ##.##.Also point here to note is since it is time it ends at 60 minutes and hence I have to break the current iteration with the help of .6.
Map map = new LinkedHashMap();
//Edit:Moved DecimalFormat Outside the loop
DecimalFormat df = new DecimalFormat("##.##");
for (double time= 06.00; time<= 22.00; time= time+ 01.00)
{
String timeString = df.format(appointmentTime);
time = Double.parseDouble(timeString);
if (timeString.indexOf(".6") != -1)
{
time= time+ 00.40;
}
map.put(time,"<number>");
}
I beliI believe you choose the most complicated approach. Instead of iterating the time variable you could iterate a simple number indicating the minutes since 0 o’clock and then generate your time double only for the map.
for(int totalMinutes = 6 * 60; totalMinutes <= 22 * 60; totalMinutes ++) {
map.put(buildTimeDouble(totalMinutes),”<number>”);
}
But I believe (I do not understand your question in that point), it would be better not to use a double for the map key, instead you could use your own Time class, something like:
Time{
private int hour;
private int minutes;
public Time(int hour; int minutes) {
this.hour = hour;
this.minutes = minutes;
}
public toString(){
return hour + “:” + minutes
}
public static Time fromTotalMinutes(int totalMinutesSinceZeroOclock){
return new Time(totalMinutesSinceZeroOclock / 60; totalMinutesSinceZeroOclock / 60);
}
}
If you are worried about performance, one modification you should make to your code is to construct the DecimalFormat just once, outside of the loop. The same instance can be reused over and over again inside the loop.
It's not a good approach to pre compute for all these values.Perhaps you can use a LRU cache.This can be easily implemented using a LinkedHashMap.I also think that you should not be using a double to represent time.Go through this article,it might give you some new ideas.
You should not work with doubles as counters, as rounding errors will creep in giving off-by-one errors.
Instead have an integer counter from which you calculate the times you need.
This question already has answers here:
How do I time a method's execution in Java?
(42 answers)
Closed 4 years ago.
What's a simple/easy way to access the system clock using Java, so that I can calculate the elapsed time of an event?
I would avoid using System.currentTimeMillis() for measuring elapsed time. currentTimeMillis() returns the 'wall-clock' time, which may change (eg: daylight savings, admin user changing the clock) and skew your interval measurements.
System.nanoTime(), on the other hand, returns the number of nanoseconds since 'some reference point' (eg, JVM start up), and would therefore not be susceptible to system clock changes.
This is some sample code.
long startTime = System.currentTimeMillis();
// Run some code;
long stopTime = System.currentTimeMillis();
System.out.println("Elapsed time was " + (stopTime - startTime) + " miliseconds.");
Apache Commons-Lang also has the StopWatch class suited just for your purpose. It uses System.currentTimeMillis(), so you'll still have resolution problems, but you can pause and do lap times and such. I use it as standard now for event stats.
http://commons.apache.org/lang/api-release/org/apache/commons/lang/time/StopWatch.html
The Answer by Leigh is correct.
java.time
Java 8 and later has the java.time framework built in.
An Instant is a moment on the timeline in UTC with nanosecond resolution (up to 9 digits of a decimal fraction of a second). The now method grabs the current date-time moment.
Instant now = Instant.now();
2016-03-12T04:29:39.123Z
You can calculate the elapsed time between a pair of Instant objects as a Duration. The duration uses nanosecond resolution with a maximum value of the seconds that can be held in a long. This is greater than the current estimated age of the universe.
Duration duration = Duration.between( startInstant , stopInstant );
The default output of Duration::toString is in standard ISO 8601 format. You can also ask for a total count of nanoseconds (toNanos) or milliseconds (toMillis), as well as other amounts.
Java 8
In Java 8, fetching the current moment resolves only to millisecond resolution (up to 3 digits of a decimal fraction of a second). So while the java.time classes can store nanoseconds they can only determine the current moment with milliseconds. This limitation is due to a legacy issue (the default Clock implementation uses System.currentTimeMillis()).
Java 9
In Java 9 and later, the default Clock implementation can determine the current moment in up to nanosecond resolution. Actually doing so depends on the fineness of your computer’s clock hardware.
See this OpenJDK issue page for more info: Increase the precision of the implementation of java.time.Clock.systemUTC()
Micro Benchmark
If your purpose is benchmarking, be sure to look at other Questions such as:
How do I write a correct micro-benchmark in Java?
Create quick/reliable benchmark with java?
Frameworks are available to assist with short-duration benchmarking.
java.lang.System.currentTimeMillis() or java.lang.System.nanoTime() ought to work to measure elapsed time.
Here is a small StopWatch class I wrote using the System.nanoTime() as suggested in the answer from Leigh:
public class StopWatch {
// Constructor
public StopWatch() {
}
// Public API
public void start() {
if (!_isRunning) {
_startTime = System.nanoTime();
_isRunning = true;
}
}
public void stop() {
if (_isRunning) {
_elapsedTime += System.nanoTime() - _startTime;
_isRunning = false;
}
}
public void reset() {
_elapsedTime = 0;
if (_isRunning) {
_startTime = System.nanoTime();
}
}
public boolean isRunning() {
return _isRunning;
}
public long getElapsedTimeNanos() {
if (_isRunning) {
return System.nanoTime() - _startTime;
}
return _elapsedTime;
}
public long getElapsedTimeMillis() {
return getElapsedTimeNanos() / 1000000L;
}
// Private Members
private boolean _isRunning = false;
private long _startTime = 0;
private long _elapsedTime = 0;
}