Android Vitals - What time is it?
Tags: android, vitals, performance, monitoring
Published: 2020-07-13 17:58:31


> Note: the pretty header photo is [from Romain Guy](https://www.flickr.com/photos/romainguy/48539648237/).

Yesterday I had an idea:

{% twitter 1282441310201237504 %}

I received enthusiastic replies and decided to just start writing. This blog series will be focused on stability and performance monitoring of Android apps in production. It is entitled **Android Vitals** because it closely relates to Google's own [Android vitals](https://developer.android.com/topic/performance/vitals): 

> Android vitals is an initiative by Google to improve the stability and performance of Android devices. When an opted-in user runs your app, their Android device logs various metrics, including data about app stability, app startup time, battery usage, render time, and permission denials.

If you have questions or suggestions for future blogs, don't hesitate to reach out [on Twitter](https://twitter.com/Piwai)!

To warm up, I'll start with a simple question:

# What time is it?

To track performance, we need to measure **time intervals**, i.e. the difference between two points in time. The JDK provides us with 2 ways to get the current time:

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

Android provides a `SystemClock` class which adds a few more:

```kotlin
// (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 pick? The `SystemClock` [javadoc](https://developer.android.com/reference/android/os/SystemClock) helps answer that question:

* [System#currentTimeMillis](https://developer.android.com/reference/java/lang/System#currentTimeMillis()) can be set by the user or the phone network, so the time may jump backwards or forwards unpredictably. **Interval or elapsed time measurements should use a different clock**.
* [SystemClock#uptimeMillis](https://developer.android.com/reference/android/os/SystemClock#uptimeMillis()) stops when the system enters deep sleep. This is the basis for most interval timing such as Thread#sleep(long), Object#wait(long), and System#nanoTime. This clock is **suitable for interval timing when the interval does not span device sleep**.
* [SystemClock#elapsedRealtime](https://developer.android.com/reference/android/os/SystemClock#elapsedRealtime()) and [SystemClock#elapsedRealtimeNanos](https://developer.android.com/reference/android/os/SystemClock#elapsedRealtimeNanos()) include deep sleep. This clock is the **recommended basis for general purpose interval timing**.

The performance of an app has no influence over what happens in deep sleep, so our best options are `SystemClock.uptimeMillis()` and `System.nanoTime()`

# `uptimeMillis()` vs `nanoTime()`

`System.nanoTime()` is more precise than `uptimeMillis()`, but that's only useful for micro-benchmarks. When tracking performance in production, we need millisecond resolution.

Let's compare their performance impact. I cloned the [Android Benchmark Samples](https://github.com/android/performance-samples) repository and added the following test:

```kotlin
@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 a Pixel 3 running Android 10:

* `System.nanoTime()` median time: 208 ns
* `SystemClock.uptimeMillis()` median time: 116 ns

`SystemClock.uptimeMillis()` is almost twice as fast! While that difference should not have any meaningful impact on an app, can we figure out why it's much faster?

## `uptimeMillis()` implementation

`SystemClock.uptimeMillis()` is implemented as a native method annotated with `@CriticalNative`. [CriticalNative](https://cs.android.com/android/platform/superproject/+/master:libcore/dalvik/src/main/java/dalvik/annotation/optimization/CriticalNative.java;l=99;drc=1a6015c8486695c938e1315403965e19997eaed8) provides faster JNI transitions for methods that contain no objects.

```java
public final class SystemClock {
    @CriticalNative
    native public static long uptimeMillis();
}
```
([source](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/SystemClock.java;l=178;drc=bc3d8b9071d4f0b2903d6836770d974e70366290))

The native implementation is in `SystemClock.cpp`:

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

([source](https://cs.android.com/android/platform/superproject/+/master:system/core/libutils/SystemClock.cpp;l=40-44;drc=bab16584ce0525742b5370682c9132b2002ee110))

`systemTime()` is defined in `Timers.cpp`:

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

([source](https://cs.android.com/android/platform/superproject/+/master:system/core/libutils/Timers.cpp;l=35-44;drc=842e1cc17efca17f4a91b0bc678b39a1b1b43da4))

## `nanoTime()` implementation

`System.nanoTime()` is also implemented as a native method annotated with `@CriticalNative`.

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

([source](https://cs.android.com/android/platform/superproject/+/master:libcore/ojluni/src/main/java/java/lang/System.java;l=345;drc=972664ff05e2531e8b37b572b3fb310077999868))

The native implementation is in `System.c`:

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

([source](https://cs.android.com/android/platform/superproject/+/master:libcore/ojluni/src/main/native/System.c;l=254-258;drc=c5f1494e3f0cdf6333bd0ba26bfac68da62e52d4))

The two implementations are actually very similar, both call `clock_gettime()`.

Turns out, `@CriticalNative` was only [recently added](https://cs.android.com/android/_/android/platform/libcore/+/b3d1a7e9d8b92b5d3295a995c10836b769fa2600) to `System.nanoTime()`, which explains why it was slower!

# Conclusion

When tracking performance in production apps:

* A **millisecond resolution** is enough for most use cases.
* To measure **time intervals**, use either `SystemClock.uptimeMillis()` or `System.nanoTime()`. The latter is slower on older Android versions but that doesn't matter here.
* I prefer `SystemClock.uptimeMillis()` as I relate to milliseconds more easily.
  * 100 ms is the limit where humans stop feeling that they are directly manipulating objects in the UI (ie having an "intuitive" experience), and instead start feeling that they are ordering a computer to do the action for them and then waiting for an answer ([source](https://web.dev/rail/#ux))
  * It's easy to remember that 100 ms is 1/10th of a second. I don't have the same quick frame of reference for nanoseconds, I have to remember that 1 ms = 1,000,000 ns and then do math.
* `SystemClock` is not in the JDK, so if you're writing portable code then `System.nanoTime()` will do just fine.