Details of various Time API in Android
- 2021-12-21 05:10:07
- OfStack
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.