Busy expectation, sleep and accuracy

Now I am trying to write a simple renderer that will call a rendering method at a frequency of 60 Hz and sleep extra time to save processor cycles for someone else.

I ran into one simple problem with the following

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;
}

which works fine but is far from 60 frames per second, but instead it jumps around the values

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

When I try to use wait expectation, the result will be much more consistent and closer to my desired fps target.

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

I would like to avoid this, albeit for an understandable processor flaw. I have the idea of ​​sleeping a bit less than necessary, and looping around for the rest of the time to be exact, but that seems a little awkward.

, - , - ?

.

,

. System.nanoTime(); , , - .

. , ,

+4
2

, :

ScheduledExecutorService

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 , , , , 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 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 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

, , - - :)

,

0

, , , .

-

long basetime = System.nanoTime();
for (int i=0; m_Running; ++i) {
    ...
    nextRedraw = baseTime + i * m_RedrawTimeout;
}

, , FPS, ( , 1%).


, , , , , - , . FPS.

AFAIK Thread.sleep .

+1

All Articles