Details of various Time API in Android

  • 2021-12-21 05:10:07
  • OfStack

Directory 1, Time API2, uptimeMillis () vs nanoTime () 3, uptimeMillis () Implementation 4, nanoTime () Implementation

1. Time API

In order to track performance, we need to measure the time interval, that is, the difference between two time points. JDK Provides us with two ways to get the current time:


// Milliseconds since Unix epoch (00:00:00 UTC on 1 January 1970)
System.currentTimeMillis()
// Nanoseconds since the VM started.
System.nanoTime()

Android 1 is provided SystemClock Class, which adds one more:


// (API 29) Clock that starts at Unix epoch.
// Synchronized using the device's location provider.
SystemClock.currentGnssTimeClock()
// Milliseconds running in the current thread.
SystemClock.currentThreadTimeMillis()
// Milliseconds since boot, including time spent in sleep.
SystemClock.elapsedRealtime()
// Nanoseconds since boot, including time spent in sleep.
SystemClock.elapsedRealtimeNanos()
// Milliseconds since boot, not counting time spent in deep sleep.
SystemClock.uptimeMillis()

Which one should we choose? SystemClock Adj. javadoc Helps answer this question:

System#currentTimeMillis It can be set by the user or the telephone network, so time can jump backwards or forwards unpredictably. Different clocks should be used for interval or elapsed time measurement.

SystemClock#uptimeMillis Stop when the system goes into deep sleep. This is the basis of most interval timing, such as Thread#sleep(long) , Object#wait(long) And System#nanoTime . The clock is suitable for interval timing when the interval does not sleep across the device.

Android 0 And SystemClock#elapsedRealtimeNanos Including deep sleep. This clock is the recommended basis for general interval timing.

The performance of the application has no effect on what happens in deep sleep, so our best choice is SystemClock.uptimeMillis() And System.nanoTime()

2. uptimeMillis () vs nanoTime ()

System.nanoTime() Ratio uptimeMillis() It is more accurate, but it is only useful for micro-benchmarking. When tracking performance in production, we need millisecond resolution.

Let's compare their performance impact. I cloned Android Benchmark Samples Repository and added the following tests:


@LargeTest
@RunWith(AndroidJUnit4::class)
class TimingBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()

    @Test
    fun nanoTime() {
        benchmarkRule.measureRepeated {
            System.nanoTime()
        }
    }

    @Test
    fun uptimeMillis() {
        benchmarkRule.measureRepeated {
            SystemClock.uptimeMillis()
        }
    }
}

Results on Pixel 3 running Android 10:

System.nanoTime() Median time: 208 ns

SystemClock.uptimeMillis() Median time: 116 ns

SystemClock.uptimeMillis() Almost twice as fast! While this difference should not have any meaningful impact on the application, can we figure out why it is so much faster?

3. Implementation of uptimeMillis ()

SystemClock.uptimeMillis() Implemented with a @CriticalNative Gets or sets the native method of the. CriticalNative Provides faster JNI conversion for methods that do not contain objects.


public final class SystemClock {
    @CriticalNative
    native public static long uptimeMillis();
}

Native implementation in SystemClock.c++ Medium:


int64_t uptimeMillis()
{
    int64_t when = systemTime(SYSTEM_TIME_MONOTONIC);
    return (int64_t) nanoseconds_to_milliseconds(when);
}

systemTime () is defined in Timers. cpp:


nsecs_t systemTime(int clock) {
    static constexpr clockid_t clocks[] = {
        CLOCK_REALTIME,
        CLOCK_MONOTONIC,
        CLOCK_PROCESS_CPUTIME_ID,
        CLOCK_THREAD_CPUTIME_ID,
        CLOCK_BOOTTIME
    };
    timespec t = {};
    clock_gettime(clocks[clock], &t);
    return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec;
}

4. Implementation of nanoTime ()

System.nanoTime() Also implemented with a @CriticalNative Gets or sets the local method of the annotation.


public final class System {
    @CriticalNative
    public static native long nanoTime();
}

Native implementation in System. c:


static jlong System_nanoTime() {
  struct timespec now;
  clock_gettime(CLOCK_MONOTONIC, &now);
  return now.tv_sec * 1000000000LL + now.tv_nsec;
}

These two implementations are actually very similar, and both call clock_gettime() .

Facts have proved that ,@CriticalNative Was recently added to the System.nanoTime() Which explains why it's slowing down!

Conclusions:

When tracking performance in production applications:

For most use cases, millisecond resolution is sufficient. To measure the time interval, use the SystemClock.uptimeMillis() Or System.nanoTime() . The latter is in the older Android The speed is slower on the version, but it doesn't matter here.


Related articles: