singerofwords
singerofwords

Reputation: 41

How to Reduce Thread.sleep() Latency in Android

I am generating timing events in a loop in a (non-UI) thread in an Android application, and I need those events to occur at precise intervals in time, (precise here means not varying any more than +/- 5 millisecs). Any errors of +/-10 millisecs (and certainly +/- 20 millisecs) can be perceived by the user. At the top of this loop I do some other calculations that take a variable amount of time, but at the bottom of the loop, I need the event to occur at a pre-calculated time.

A higly simplified version (without exception handling) of one attempt at my non-UI thread is below:

public final void run() {

    long loopTime = 2500L;
    long eventTime = (System.nanoTime() / 100000L) + loopTime;

    while (true) {

        calcutionsTakingVaryingAmountOfTime(); // takes 200 millisecs or less

        long eventWait = eventTime - (System.nanoTime() / 100000L);
        Thread.sleep(eventWait / 10L);
        listener.onEvent();

        eventTime = eventTime + loopTime;
    }
}

It is the call to listener.onEvent() that needs to be precisely timed.

In the example above, the timing variables loopTime, eventTime, and eventWait measure time in tenths of a millisec. The expressions (System.nanoTime() / 100000L) measuring current time are likewise in tenths of a millisec.

I am absolutely certain that calcutionsTakingVaryingAmountOfTime() always takes less than 200 millisecs, and the call to listener.onEvent() is just a few millisecs. So as it stands, with loopTime set to 2500L, my events ought to occur every 250 millisecs.

I have intstrumented my code (not shown) to print to Log.d() the latency in the Thread.sleep() wake up time. That is, I calculate

long latency = (System.nanoTime() / 100000L) - eventTime

immediately after the return from Thread.sleep(), and print it to Log.d().

When I run this in the emulator, what I find is that latency (after dividing by 10 to get the result into millisecs) is normally jumping between 1 and 50 millisecs in successive passes through the loop, with occasional values as high as a half a second. When run on an actual device, things are quite a bit better, but still a little wobbly (and even still, the emulator behavior makes me wonder if this is going to happen on users' devices).

To try to steady my event and control the latency, I tried several other approaches:

But there was no improvement at all in the latency with these.

The one approach that steadys the event, and reduces the latency to less than 2 or 3 millisecs, and rarely hiccups is to replace the Thread.sleep() call by a polling loop:

while ((System.nanoTime() / 100000L) < eventTime)
    ;

On the one hand, I feel embarrassed spending machine cycles like a drunken sailor on liberty. On the other hand, I am beginning to think there is no better way, and I should burn the machine cycles in the polling loop to reduce my latency, and meet my specification. Of course, when my app goes to the background, I pause my thread, so this polling loop works. But what a waste.

Any ideas would be greatly appreciated.

Upvotes: 4

Views: 1798

Answers (1)

Salw
Salw

Reputation: 1900

I'm using delayed messages with Handler for similar purpose. It can be little overkill. In you case I would take a look to Timer class.

mTimer = new Timer();
mTimer.scheduleAtFixedRate(new TimerTask() {
    @Override
    public void run() {
        Log.v("TEST", " tick");
    }
}, 0, 250);

This gives me latency +-2 millisecs at emulator.

Upvotes: 2

Related Questions