안드로이드 시간 측정

성능 측정

long startTime = getCurrentTime();
// do something
long endTime = getCurrentTime();
Log.d(DEBUG, "elapsedTime : " + (endTime - startTime));

일반적으로 성능을 측정할때는 어떤 기능을 수행하는데 걸리는 시간을 측정합니다. 안드로이드에서도 역시 어떤일을 하는데 시간이 얼마나 소요되었는지를 측정해야 할 때가 있습니다 예를 들면 이 코드는 어떤 기능을 수행하는데 소요된 시간을 보여줍니다.

Java

Java에서는 시스템의 시간을 가져오는데 기본적으로 System.currentTimeMillis(), System.nanoTime()을 제공합니다.

System.currentTimeMillis()

System.currentTimeMillis()1970/1/1 00:00:00 UTC을 기준으로 현재 시간을 리턴합니다. 시스템의 TimeZone에 관계없이 항상 UTC 시간을 리턴하기 때문에 보통 "Unix time", "epoch time"이라고 부릅니다. System.currentTimeMillis()는 시스템의 시간을 가져오지만, 시스템의 시간은 언제든지 변경될수 있기 때문에 시간 측정에는 적합하지 않습니다. (ACTION_TIME_TICK, ACTION_TIME_CHANGED, ACTION_TIMEZONE_CHANGED과 같은 브로드캐스트에 의해 시간이 변경되는걸 알 수 있습니다.)

System.nanoTime()

System.nanoTime()은 로컬 시스템에서 가장 정확한 현재 timestamp를 나노초 단위로 리턴합니다. (리눅스의 CLOCK_MONOTONIC과 같습니다) 정확한 현재 시간을 나타내는건 아니므로, 시간을 측정할때만 사용되어야 하며, 같은 프로세스, 같은 디바이스 간의 timestamp와 비교하여야 합니다. 현재 시간을 알고 싶으면 System.currentTimeMillis()을 사용해야 합니다.

Android

안드로이드에서는 SystemClock.currentThreadTimeMillis(), SystemClock.elapsedRealtime(), SystemClock.elapsedRealtimeNanos()Debug.threadCpuTimeNanos()를 제공합니다.

SystemClock.currentThreadTimeMillis()

SystemClock.currentThreadTimeMillis()는 현재 스레드의 시간을 밀리초 단위로 리턴합니다.

SystemClock.elapsedRealtime()

SystemClock.elapsedRealtime(), SystemClock.elapsedRealtimeNanos()는 부팅 이후의 시간을 리턴합니다. 이때 sleep으로 소모된 시간이 포함됩니다.

SystemClock.uptimeMillis()

SystemClock.uptimeMillis()는 부팅 이후 시간을 리턴합니다. 이때 deep sleep으로 소모된 시간은 포함하지 않습니다.

Debug.threadCpuTimeNanos()

Debug.threadCpuTimeNanos()는 현재 스레드에서 소모된 시간만 측정합니다. 현재 스레드에서 코드를 실행하거나 특정 I/O를 기다린 시간을 나노초 단위로 리턴합니다. 지원하지 않는 시스템에선 -1을 리턴할 수 있습니다. 다른 스레드에서 소모된 시간을 포함하지 않기 때문에 다른 스레드를 고려하지 않고 시간 측정을 할때 가장 정확한 시간을 측정 할 수 있습니다.

성능측정

위의 6개의 메소드를 이용하여 시간을 측정해 보겠습니다.

long currentTimeMillisStart = System.currentTimeMillis();
long nanoTimeStart = System.nanoTime();
long currentThreadTimeMillisStart = SystemClock.currentThreadTimeMillis();
long elapsedRealtimeStart = SystemClock.elapsedRealtime(); 
long uptimeMillisStart = SystemClock.uptimeMillis(); 
long threadCpuTimeNanosStart = Debug.threadCpuTimeNanos(); 

// some code 
try {
  Thread.sleep(100);
} catch (InterruptedException e) {
  e.printStackTrace();
} 

long currentTimeMillisEnd = System.currentTimeMillis(); 
long nanoTimeEnd = System.nanoTime(); 
long currentThreadTimeMillisEnd = SystemClock.currentThreadTimeMillis(); 
long elapsedRealtimeEnd = SystemClock.elapsedRealtime(); 
long uptimeMillisEnd = SystemClock.uptimeMillis(); 
long threadCpuTimeNanosEnd = Debug.threadCpuTimeNanos();
결과값
09-22 15:05:21.306 System.currentTimeMillis();  -> 138ms
09-22 15:05:21.306 System.nanoTime();  -> 138.020823ms
09-22 15:05:21.306 SystemClock.currentThreadTimeMillis(); -> 30ms 
09-22 15:05:21.306 SystemClock.elapsedRealtime(); -> 138ms
09-22 15:05:21.306 SystemClock.uptimeMillis(); -> 138ms
09-22 15:05:21.306 Debug.threadCpuTimeNanos(); -> 29.924149ms

결과값을 보게되면 30ms, 138ms 두가지 값이 측정 되었습니다.
약 30ms로 측정 된 SystemClock.currentThreadTimeMillis(), Debug.threadCpuTimeNanos()는 다른 스레드의 작업을 수행하는데 걸리는 시간을 제외한, 현재 스레드에 소요된 시간을 보여주는 것을 알 수 있습니다.
위의 모든 메소드에서 리턴값이 밀리초, 나노초 단위여도 그정도 수준의 정확도를 보장하는것은 아닙니다.

요약

System.currentTimeMillis()는 시간 측정용이 아니므로 다른 메소드를 사용할것. SystemClock.currentThreadTimeMillis(), Debug.threadCpuTimeNanos()는 스레드가 동작하는 시간 만 포함이된다.

[참고자료]

Juyeong Lee

구직중입니다. https://www.linkedin.com/in/juyeong/