我现在正在尝试编写一个简单的渲染器,它将以60Hz的频率调用渲染方法,并为其他人节省额外的时间来节省CPU周期。
我遇到了以下
的一个简单问题while(m_Running){
//start meassuring whole frame time
t0 = System.nanoTime();
render();
//meassure time spent rendering
t1 = System.nanoTime();
if(t1<nextRedraw){
try{
diff = nextRedraw-t1;
ms = diff/1000000;
ns = (int) (diff-(ms*1000000));
Thread.sleep(ms, ns);
}catch(InterruptedException e){
Logger.logWarning("Renderer interrupted!",e);
}
//while(System.nanoTime()<nextRedraw);//busy wait alternative
}
//meassure time spent sleeping
t2 = System.nanoTime();
nextRedraw = t2+m_RedrawTimeout;
long frameTime = t2-t0;
long renderTime = t1-t0;
long sleepTime = t2-t1;
long fps = 1000000000/frameTime;
}
运行良好,但远不是预期的60fps,而是它跳过值
FPS: 63 Frame: 15,7ms Render: 2,0ms Sleep: 13,7ms
FPS: 64 Frame: 15,5ms Render: 2,0ms Sleep: 13,5ms
FPS: 63 Frame: 15,7ms Render: 2,1ms Sleep: 13,5ms
FPS: 59 Frame: 16,7ms Render: 2,8ms Sleep: 14,0ms
FPS: 64 Frame: 15,5ms Render: 2,2ms Sleep: 13,3ms
当我尝试使用忙等待时,结果更加一致并且更接近我想要的fps目标。
FPS: 60 Frame: 16,4ms Render: 2,0ms Sleep: 14,5ms
FPS: 60 Frame: 16,4ms Render: 2,0ms Sleep: 14,4ms
FPS: 60 Frame: 16,4ms Render: 2,0ms Sleep: 14,4ms
FPS: 61 Frame: 16,3ms Render: 2,4ms Sleep: 13,8ms
FPS: 61 Frame: 16,3ms Render: 2,1ms Sleep: 14,2ms
FPS: 60 Frame: 16,4ms Render: 2,0ms Sleep: 14,4ms
我想避免这种情况,因为可以理解的CPU缺点。我知道睡觉的时间少于必要的时间并且在剩下的时间内循环以确切但这看起来有点笨拙。
我的问题是,是否忙于等待某种方式通过编译器优化,或者是否有其他方法来达到类似的时间?
任何帮助都将不胜感激。
亲切的问候,
Vojtěch
注意:我使用了System.nanoTime();为了使事情更准确,它没有帮助,但我不知道它有任何性能缺陷
注意:我知道睡眠非常不准确,但我没有找到任何其他选择
答案 0 :(得分:1)
我想,时间的微小差异可能会增加更多的数量,你可以通过让睡眠取决于总时间来轻松避免它。
像
这样的东西long basetime = System.nanoTime();
for (int i=0; m_Running; ++i) {
...
nextRedraw = baseTime + i * m_RedrawTimeout;
}
如果当前的一个太短而反之亦然,这应该使后续睡眠更短,所以你得到的FPS每秒最多只变化几毫秒(即<1%)。
也许没有问题,只是你测量的是单帧占用多长时间。这个数字有点不同,并没有多说FPS费率。
AFAIK Thread.sleep
中的纳秒被完全忽略。
答案 1 :(得分:0)
因此,我在以前发布的代码之上完成了建议并测试了这三种变体:
private final ScheduledExecutorService scheduler
= Executors.newScheduledThreadPool(1);
//handle for killing the process
private ScheduledFuture<?> handle = null;
//meassuring actual time between redraws
private long lastRenderStart = 0;
//start will fire off new scheduler at desired 60Hz
@Override
public synchronized void start() {
handle = scheduler.scheduleAtFixedRate(this,
100000000, m_RedrawTimeout, TimeUnit.NANOSECONDS);
}
public void kill(){
if(handle!=null){
handle.cancel(true);
}
}
@Override
public void run(){
//meassured render routine
long t0 = System.nanoTime();
render();
long t1 = System.nanoTime();
System.out.format("render time %.1fms\t\tframe time %.1fms\n",
((t1-t0)/1000000.0d),
((t0-lastRenderStart)/1000000.0d));
lastRenderStart = t0;
}
这使代码更简单,AFAIK没有引入任何开销,但精度仍然没有达到我想要的程度
render time 1,4ms frame time 17,0ms
render time 1,4ms frame time 16,0ms
render time 1,7ms frame time 17,0ms
render time 1,3ms frame time 17,0ms
render time 1,8ms frame time 16,0ms
render time 14,8ms frame time 16,9ms
render time 2,0ms frame time 17,0ms
long nextRedraw,baseTime = System.nanoTime();
m_Running=true;
for (long i=0; m_Running; ++i) {
long t0 = System.nanoTime();
render();
long t1 = System.nanoTime();
nextRedraw = baseTime + i * m_RedrawTimeout;
long now = System.nanoTime();
if(now<nextRedraw){
try{
Thread.sleep((nextRedraw-now)/1000000);
}catch(InterruptedException e){
Logger.logWarning("Renderer interrupted!",e);
}
}
long t2 = System.nanoTime();
long frameTime = t2-t0;
long renderTime = t1-t0;
long sleepTime = t2-t1;
long fps = 1000000000/frameTime;
System.out.format("FPS: %d\tFrame: %3.1fms\t"
+"Render: %3.1fms\tSleep: %3.1fms\n",
fps, frameTime/1000000.0, renderTime/1000000.0, sleepTime/1000000.0);
}
这种方法通常会将帧时间保持在16.67 ms左右,并且看起来比最初发布的代码更优雅,但仍有1ms的峰值(无论出于何种原因,可能在系统调度程序中舍入?)
FPS: 60 Frame: 16,6ms Render: 1,7ms Sleep: 14,9ms
FPS: 60 Frame: 16,6ms Render: 1,8ms Sleep: 14,8ms
FPS: 63 Frame: 15,7ms Render: 2,3ms Sleep: 13,4ms
FPS: 59 Frame: 16,7ms Render: 1,8ms Sleep: 14,8ms
FPS: 63 Frame: 15,6ms Render: 2,0ms Sleep: 13,7ms
FPS: 60 Frame: 16,7ms Render: 1,9ms Sleep: 14,7ms
FPS: 60 Frame: 16,6ms Render: 1,9ms Sleep: 14,7ms
FPS: 59 Frame: 16,8ms Render: 1,8ms Sleep: 15,0ms
FPS: 64 Frame: 15,6ms Render: 1,9ms Sleep: 13,7ms
FPS: 60 Frame: 16,6ms Render: 1,9ms Sleep: 14,7ms
FPS: 60 Frame: 16,6ms Render: 1,8ms Sleep: 14,8ms
在这个中我尝试将忙等待段添加到循环中,以便在将线程休眠到精确帧率后等待剩余时间。
long nextRedraw,baseTime = System.nanoTime();
m_Running=true;
for (long i=0; m_Running; ++i) {
long t0 = System.nanoTime();
render();
long t1 = System.nanoTime();
nextRedraw = baseTime + i * m_RedrawTimeout;
if(t1<nextRedraw){
//if sleepy time is bigger than 1ms, use Thread.sleep
if(nextRedraw-t1>1000000){
try{
Thread.sleep(((nextRedraw-t1-1000000)/1000000));
}catch(InterruptedException e){
Logger.logWarning("Renderer interrupted!",e);
}
}
t2 = System.nanoTime();
//do busy wait on last ms or so
while(System.nanoTime()<nextRedraw);
}
long t3 = System.nanoTime();
long frameTime = t3-t0;
long renderTime = t1-t0;
long sleepTime = t2-t1;
long busyWaitTime = t3-t2;
long fps = 1000000000/frameTime;
System.out.format("FPS: %d\tFrame: %3.1fms\t"
+ "Render: %3.1fms\tSleep: %3.1fms\tBusyW: %3.1fms\n",
fps, frameTime/1000000.0, renderTime/1000000.0,
sleepTime/1000000.0,busyWaitTime/1000000.0);
}
看起来有点凌乱,对不起,但是在交换大约等待2毫秒的CPU时间时,我在60FPS上获得了相当稳定的时机。
FPS: 60 Frame: 16,5ms Render: 1,5ms Sleep: 13,8ms BusyW: 1,2ms
FPS: 60 Frame: 16,5ms Render: 1,4ms Sleep: 13,2ms BusyW: 1,8ms
FPS: 60 Frame: 16,4ms Render: 1,6ms Sleep: 12,4ms BusyW: 2,5ms
FPS: 60 Frame: 16,5ms Render: 1,2ms Sleep: 13,1ms BusyW: 2,1ms
FPS: 60 Frame: 16,5ms Render: 1,4ms Sleep: 13,3ms BusyW: 1,8ms
FPS: 60 Frame: 16,4ms Render: 1,4ms Sleep: 12,5ms BusyW: 2,5ms
FPS: 60 Frame: 16,5ms Render: 1,2ms Sleep: 13,1ms BusyW: 2,2ms
FPS: 60 Frame: 16,5ms Render: 1,3ms Sleep: 13,4ms BusyW: 1,8ms
FPS: 60 Frame: 16,4ms Render: 1,5ms Sleep: 12,5ms BusyW: 2,5ms
非常感谢大家的建议,希望这可以帮助某些人:)
亲切的问候,
Vojtěch