所以我浏览了建议并在之前发布的代码基础上测试了这三个变体:
ScheduledExecutorService
private final ScheduledExecutorService scheduler
= Executors.newScheduledThreadPool(1);
private ScheduledFuture<?> handle = null;
private long lastRenderStart = 0;
@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(){
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;
}
这使得代码变得更简单了,而且据我所知也没有引入任何开销,但精度仍然没有达到我想要的程度。
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毫秒左右,看起来比最初发布的代码更加优雅一些,但仍然可能出现1毫秒较短的波峰(可能是系统调度器中的舍入等原因)。
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(nextRedraw-t1>1000000){
try{
Thread.sleep(((nextRedraw-t1-1000000)/1000000));
}catch(InterruptedException e){
Logger.logWarning("Renderer interrupted!",e);
}
}
t2 = System.nanoTime();
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
Thread.sleep
的唤醒更多地取决于线程调度器。从技术上讲,调度器可以对任何线程进行任何操作,但它可能不太可能长时间挂起活动线程,而对于线程的计划唤醒请求则更加灵活。 - yshavit