I'm trying to keep my game at 60fps, but I'm getting strange results from my code like "2-8000 fps" Why isn't this staying at 60?
public static void main(String[] args) {
joglplat m = new joglplat();
while(true){
long startTime = System.nanoTime() / 1000000;
try
{
// 123456: 6 zeros => 16ms
long nsToSleep = 16000000 - (System.nanoTime() - lastFrame);
System.out.println("ns: " + nsToSleep);
lastFrame = System.nanoTime();
if(nsToSleep > 0)
{
System.out.println("ns2: " + (nsToSleep/1000));
System.out.println("ns3: " + (nsToSleep%1000));
Thread.sleep(nsToSleep/16000000, (int)(nsToSleep % 1000));
}
else
{
Thread.yield(); // Only necessary if you want to guarantee that
// the thread yields the CPU between every frame
}
}
catch(Exception e){
e.printStackTrace();
}
m.controls();
m.update();
m.repaint();
System.out.println("framerate: " + (System.nanoTime() / 1000000 - startTime));
}
}
Your output is the number of seconds your program has run for, not framerate. You should be dividing your frame count (which you aren't collecting) by the total time run.
To get the frame count, simply add a new variable outside of your game loop, and increment it each time through...
public static void main(String[] args) {
long frames = 0;
joglplat m = new joglplat();
while(true){
frames++;
// other code here
System.out.println("framerate: " + ((System.nanoTime() / 1000000 - startTime) / frames ) );
}
}
Note, however, that this will give you the average framerate throughout the entire execution of your program. Two other options you have are to get the instantaneous framerate, and the average framerate over the past N frames.
All styles in one (untested/uncompiled, so might have some errors, but should get you started in the right direction):
public static void main(String[] args) {
long startTime = System.nanoTime();
long lastFrameTime = startTime;
long frames = 0;
int framesToAverage = 10;
long[] frameTimes = new long[framesToAverage];
joglplat m = new joglplat();
while(true){
// logic here
long currentFrameDuration = System.nanoTime() - lastFrame;
lastFrameTime = System.nanoTime();
long instantFramerate = currentFrameDuration / 1000000;
int currentFrameIndex = frames % frameTimes.length;
frameTimes[currentFrameIndex] = currentFrameDuration;
frames++;
long averageFramerate = ( ( lastFrameTime - startTime) / frames ) / 1000000;
long instantFramerate = currentFrameDuration / 1000000;
if( frames > frameTimes.length ) { // if it isn't, we don't have enough data yet
int firstFrameIndex = currentFrameIndex + 1;
if( firstFrameIndex > frameTimes.length ) {
firstFrameIndex = 0;
}
long averageFrameratePerN = ( ( frameTimes[currentFrameIndex] - frameTimes[firstFrameindex] ) / frameTimes.length ) / 1000000;
}
// yield/sleep here
}
}
My suspicion is that this is caused by the inaccuracy of Thread.sleep():
Causes the currently executing thread
to sleep (cease execution) for the
specified number of milliseconds plus
the specified number of nanoseconds,
subject to the precision and accuracy
of system timers and schedulers. The
thread does not lose ownership of any
monitors.
Is there any reason why you have to hold the framerate down like this? Perhaps you can explain more fully what you are trying to accomplish?
Related
I have a java game wherein there is a countdown timer from 60 secs. It works fine, but when I pause the game and return back to play state. the original time is now reduced because I found out that the timer still keeps running from the system no matter what gameState I'm in. How do I fix this?
I tried storing the remainingTime to a pauseTime variable whenever I switch states, and just subtract it and stuff. But my efforts seem to failed.
// GET ELAPSED TIME
if(gp.gameState == gp.playState && remainingTime >= 0) {
soundCounter--;
elapsedTime = System.currentTimeMillis() - startTime;
remainingTime = totalTime - elapsedTime;
if(remainingTime <= 0) {
remainingMilliseconds = 0;
remainingSeconds = 0;
remainingMinutes = 0;
// GAME OVER
gp.gameOver();
} else {
remainingMilliseconds = remainingTime % 1000;
remainingSeconds = (remainingTime / 1000) % 60;
remainingMinutes = (remainingTime / 1000) / 60;
}
}
// DRAW TIMER
timeString = String.format("%02d:%02d:%03d", remainingMinutes, remainingSeconds, remainingMilliseconds);
Your problem is that you are calculating your elapsed time forever since the first startTime.
One of the solution would be to calculate a delta (difference) time between loop iterations.
Here some very primitive code that should get you started:
public static void main(String[] args) {
boolean paused = false;
long lastRun = System.currentTimeMillis();
long elapsed = 0;
System.out.println("Game start");
while (true) {
long now = System.currentTimeMillis();
long delta = now - lastRun;
if (!paused && delta > 0) {
elapsed += delta;
// Do game stuff
System.out.println("Elapsed: " + elapsed);
if (elapsed >= 5000) return;
lastRun = now;
}
}
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 months ago.
Improve this question
Summary
I am doing a million floating point divisions in different threads, these threads share nothing from programmer's point of view i.e. no explicit locks are involved.
Following are the perf numbers when I ran java -jar /tmp/my-exps-1.0-SNAPSHOT.jar 1000000 100 on machines having 8, 16 and 32 vcores (n/2 cores with 2 threads per core).
1000000 is number of floating point divisions that each thread does, 100 is number of threads.
All the processors belonged to same family - Intel(R) Xeon(R) Platinum 8175M CPU # 2.50GHz.
Using htop, I was seeing a 100% usage for all the cores when the program was running.
====================================
runtime.availableProcessors() = 8
=====runBenchmark() FINISHED in 3156601 millis ======== average time to complete one thread = 31566.01
====================================
runtime.availableProcessors() = 16
=====runBenchmark() FINISHED in 3297807 millis ======== average time to complete one thread = 32978.07
====================================
runtime.availableProcessors() = 32
=====runBenchmark() FINISHED in 3448590 millis ======== average time to complete one thread = 34485.9
====================================
Expectation
I expected it to scale linearly with number of cores i.e execution time should decrease in proportion to increase in CPU cores. However the above numbers are totally opposite. It increases by a small fraction with increment in vcores :D
Not very sure but some guesses that I made
Context switch time increases with number of cores ?
Some implicit locks are taken on data and those become more expensive as the race increase with number of cores.
Code Details
Thread
private static class BenchmarkThread implements Callable<BenchmarkResult> {
private final long numOperationsPerThread;
private BenchmarkThread(long numOperationsPerThread) {
this.numOperationsPerThread = numOperationsPerThread;
}
#Override
public BenchmarkResult call() {
double sum = 0;
long start = System.currentTimeMillis();
for (long i = 0; i < numOperationsPerThread; i++) {
double numerator = RANDOM.nextDouble();
double denominator = RANDOM.nextDouble();
double result = numerator / denominator;
sum += result;
}
long end = System.currentTimeMillis();
return new BenchmarkResult(Thread.currentThread().getName(),
(end - start),
sum);
}
}
Driver (yes resultFuture.get() blocks but we are not counting that time, we are doing a sum of individual thread times timeToComplete += benchmarkResult.timeToCompleteMillis)
Complete runnable example(edited - see EDIT 1 below)
public class VerticalScalingExp {
private final long numOperationsPerThread;
private final int numThreads;
private final List<Future<BenchmarkResult>> benchMarkResultsFuture;
private final ExecutorService executorService;
public VerticalScalingExp(long numOperationsPerThread, int numThreads) {
this.numOperationsPerThread = numOperationsPerThread;
this.numThreads = numThreads;
this.benchMarkResultsFuture = new ArrayList<>(numThreads);
this.executorService = Executors.newFixedThreadPool(numThreads);
}
public static void main(String[] args) throws Exception {
long numOperationsPerThread;
int numThreads;
if (args.length != 2) {
numOperationsPerThread = 1000000;
numThreads = 50;
} else {
numOperationsPerThread = Long.parseLong(args[0]);
numThreads = Integer.parseInt(args[1]);
}
new VerticalScalingExp(numOperationsPerThread, numThreads).runBenchmark();
}
private void runBenchmark() throws Exception {
try {
System.out.println("[START]====VerticalScalingExp.runBenchmark====" );
System.out.println("numOperationsPerThread = " + numOperationsPerThread + ", numThreads = " + numThreads);
Runtime runtime = Runtime.getRuntime();
System.out.println("runtime.maxMemory() = " + runtime.maxMemory());
System.out.println("runtime.freeMemory() = " + runtime.freeMemory());
System.out.println("runtime.availableProcessors() = " + runtime.availableProcessors());
long timeToComplete = 0;
for (int i = 0; i < numThreads; i++) {
benchMarkResultsFuture.add(executorService.submit(new BenchmarkThread(numOperationsPerThread)));
}
for (Future<BenchmarkResult> resultFuture : benchMarkResultsFuture) {
BenchmarkResult benchmarkResult = resultFuture.get();
System.out.println("resultFuture.get() = " + benchmarkResult);
timeToComplete += benchmarkResult.timeToCompleteMillis;
}
double avg = (double) timeToComplete / numThreads;
System.out.println("=====runBenchmark() FINISHED in " + timeToComplete +
" millis ======== average time to complete one thread = " + avg );
} finally {
executorService.shutdown();
}
}
private static class BenchmarkThread implements Callable<BenchmarkResult> {
private final long numOperationsPerThread;
private BenchmarkThread(long numOperationsPerThread) {
this.numOperationsPerThread = numOperationsPerThread;
}
#Override
public BenchmarkResult call() {
double sum = 0;
long start = System.currentTimeMillis();
ThreadLocalRandom random = ThreadLocalRandom.current();
for (long i = 0; i < numOperationsPerThread; i++) {
double numerator = random.nextDouble();
double denominator = random.nextDouble();
double result = numerator / denominator;
sum += result;
}
long end = System.currentTimeMillis();
return new BenchmarkResult(Thread.currentThread().getName(),
(end - start),
sum);
}
}
private static class BenchmarkResult {
private final String threadName;
private final long timeToCompleteMillis;
private final double sum;
public BenchmarkResult(String threadName, long timeToCompleteMillis, double sum) {
this.threadName = threadName;
this.timeToCompleteMillis = timeToCompleteMillis;
this.sum = sum;
}
#Override
public String toString() {
return "BenchmarkResult{" +
"threadName='" + threadName + '\'' +
", timeToCompleteMillis=" + timeToCompleteMillis +
", sum =" + sum +
'}';
}
}
}
Questions
I found this blog - https://www.theserverside.com/feature/Why-Java-Applications-Fail-to-Scale-Linearly which explains very nicely about data collisions that happen with increase in number of cores, is the code above suffering from the same ? (But in my code I am not sharing anything among threads ?) If yes, at what level these collisions are happening, Heap ? CPU caches ? something else ?
Is this a commonly observed pattern ? (perf does not scale linearly with cpu cores)
What can be done to make it scale as expected ?
Apologies for the long post :) Thanks :)
EDIT 1:
As suggested in comments and answer, I tried using ThreadLocalRandom.
Performance increases a lot as compared to previous cases but still, performance decreases with increase in cores.
====================================
runtime.availableProcessors() = 8
=====runBenchmark() FINISHED in 1683 millis ======== average time to complete one thread = 16.83
====================================
runtime.availableProcessors() = 16
=====runBenchmark() FINISHED in 6622 millis ======== average time to complete one thread = 66.22
====================================
runtime.availableProcessors() = 32
=====runBenchmark() FINISHED in 19924 millis ======== average time to complete one thread = 199.24
====================================
This could be the cause of the problem:
private static final Random RANDOM = new Random();
Because this is contended between all threads.
Try a ThreadLocalRandom instead.
Also, I would use a more reliable benchmarking approach like JMH.
I don't think you're measuring what you think you're measuring. You have 100 tasks and you measure how much time each task takes to finish. Suppose each takes 2sec. So if we execute them one after another it'll be 2sec * 100.
Now suppose you run them in 8 threads and 8 cores. This doesn't (ideally) change the amount of time each task takes, so you still have 2sec for each task. And you again have 2sec * 100 of summed time. But the overall execution time changes - it's (2sec * 100) / 8 because this summed time is now spread across 8 cores instead of 1.
So what you need to measure is the total time it takes for the program to run. Just measure it in runBenchmark() method:
private void runBenchmark() throws Exception {
try {
long started = System.nanoTime();
for (int i = 0; i < numThreads; i++)
benchMarkResultsFuture.add(executorService.submit(new BenchmarkThread(numOperationsPerThread)));
for (Future<BenchmarkResult> resultFuture : benchMarkResultsFuture)
resultFuture.get();
long timeToComplete = (System.nanoTime() - started) / 1000;
System.out.println("=====runBenchmark() FINISHED in " + timeToComplete);
} finally {
executorService.shutdown();
}
}
I have 2 integer values, let's say int a = 5 and int b = 4.
public static void run() {
long lastTime = System.nanoTime();
final double ns = 1000000000.0 / 60.0;//60 times per second
double delta = 0;
while(running) {
long now = System.nanoTime();
delta = delta + ((now-lastTime) / ns);
lastTime = now;
while (delta >= 1)//Make sure update is only happening 60 times a second
{
delta--;
}
}
What I want to achieve is to be able to change values of the integers within the while (delta >= 1){} every second to a number between 100 and 400. I want to create a paint method that takes the values of the integers and updates the component sizes within my JFrame. I just need an idea to go with, so I can move on.
I'm not sure if I fully understood your question. However, if your intention is to run a bit of code 60 times per second, there is definitely an easier way to accomplish this - via Java's Thread.sleep() mechanism. You can use Java's Random class to actually generate random values for a and b:
public static void run() {
Random random = new Random();
while (running) {
try {
Thread.sleep(16); // 16 millisecond sleep; will run 60 times per second
// Generate random number between 100 and 400
a = (random.nextInt() % 300) + 100;
b = (random.nextInt() % 300) + 100;
} catch (Exception e) {
System.out.println("Exception encountered when trying to sleep! " + e.toString());
}
}
}
Alternatively, you can also use Java's Timer and TimerTask classes to the same effect.
I'm working on optimizing a little java program I made for Christmas. I was testing the amount of time it takes to run the methods and one of them, Snow_AI, is taking 1234567 nano seconds to run.
The issues is with the counter method:
public boolean Count() {
if (CreateCnt > CreateTime) {
CreateCnt = 0;
return true;
} else {
CreateCnt = CreateCnt + 1;
}
return false;
}
This is how I'm making the calls:
MethTmr1 = System.nanoTime();
if (Snw.Count()) {
MethTmr = System.nanoTime();
Snw.Snow_AI();
MethTPS = 1000000000 / (System.nanoTime() - MethTmr);
}
try{
MethTPS1 = 1000000000 / (System.nanoTime() - MethTmr1);
}catch(Exception e){}
when I move the timing calls inside the If statement it changed the time to run to less than 5000. Anyone know why the counter method is causing this to happen?
Your measurement is flawed.
Check this: Why is System.nanoTime() way slower (in performance) than System.currentTimeMillis()?
And then look at your code:
MethTmr1 = System.nanoTime();
if (Snw.Count()) {
//expensive! Added to outer time
MethTmr = System.nanoTime();
Snw.Snow_AI();
//expensive! Added to outer time
MethTPS = 1000000000 / (System.nanoTime() - MethTmr);
}
try{
MethTPS1 = 1000000000 / (System.nanoTime() - MethTmr1);
}catch(Exception e){}
Baseline. Your MethTPS1 includes your (fast) getCount(), your Snow_AI() and two tcalls to System.nanoTime()
Try it this way once:
MethTmr1 = System.nanoTime();
if (Snw.Count()) {
//expensive! Added to outer time
//MethTmr = System.nanoTime();
Snw.Snow_AI();
//expensive! Added to outer time
//MethTPS = 1000000000 / (System.nanoTime() - MethTmr);
}
try{
MethTPS1 = 1000000000 / (System.nanoTime() - MethTmr1);
}catch(Exception e){}
I was creating a simple 'benchmark' program checking how many calculations a PC can do in 1 second, three times, then averaging and printing the result.
package benchmark;
public class Benchmark {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
long endTime = startTime + 1000;
long index = 0;
long index2 = 0;
long index3 = 0;
while (true) {
double x = Math.sqrt(index);
long now = System.currentTimeMillis();
if (now > endTime) {
break;
}
index++;
}
while (true) {
double y = Math.sqrt(index2);
long now2 = System.currentTimeMillis();
if (now2 > endTime) {
break;
}
while (true) {
double z = Math.sqrt(index3);
long now3 = System.currentTimeMillis();
if (now3 > endTime) {
break;
}
}
long total = (index + index2 + index3) / 3;
}
System.out.print(index + " loops per second on average.");
}
}
x, y and z variables are used here to store the result of Math.sqrt(index), and indexes 2 and 3.
However at no point in this program are the x, y and z variables called upon at all. Is there a way to remove them from the program?
A variabile uses space on the stack, and the relative push operation to write to the stack.
The compiler and/or the JIT (Just in time compiler) might (and usually will) remove those variables automatically, so having them or not having them will not make a difference if not in the first loops.
If you're mico-benchmarking you should always run the loop a few times (standard seems to be 10000 times) to warmup the code, and then re-run it again to measure real performance.
Are you looking for this solution?
package benchmark;
public class Benchmark {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
long endTime = startTime + 1000;
long index = 0;
long index2 = 0;
long index3 = 0;
while (true) {
Math.sqrt(index);
long now = System.currentTimeMillis();
if (now > endTime) {
break;
}
index++;
}
while (true) {
Math.sqrt(index2);
long now2 = System.currentTimeMillis();
if (now2 > endTime) {
break;
}
while (true) {
Math.sqrt(index3);
long now3 = System.currentTimeMillis();
if (now3 > endTime) {
break;
}
}
}
long total = (index + index2 + index3) / 3;
System.out.print(total + " loops per second on average.");
}
}
Also note, that index2 and index3 are never incremented and endTime is never recompouted, so the second and third while loop are only executed once anyway.
Yes, you can remove them, just call the method without storing the result, replace:
double x = Math.sqrt(index);
by
Math.sqrt(index);