[Android][Kotlin] Benchmark 이론

D.O·2023년 10월 9일
1

Jetpack Benchmark

Jetpack Benchmark는 Android App의 성능을 측정하는 Jetpack Library 입니다.

일반적으로 하기 쉬운 측정 실수를 줄여주게끔 도와준다고 합니

주요 특징

  • 정확한 측정: 배경 작업이나 시스템 서비스의 영향을 최소화하여 성능 측정의 정확도를 높입니다.
  • JUnit 기반: Android 개발자들에게 익숙한 JUnit 프레임워크를 기반으로 작동합니다.
  • Android Studio 통합: 벤치마크 결과를 직접 Android Studio에서 확인하고 분석할 수 있습니다.

저는 Android Jetpack의 Benchmark 라이브러가 나온지 꽤 오래되었을 줄 알았지만 2019년에 Google I/O에서 정식으로 처음 소개되었다고 합니다.

생각보다 발표 된지 오래 되지 않았네요

해당 발표 영상 링크입니다. benchmark 공부를하면서 많은 도움을 받았습니다.

Jetpack Benchmark 이전의 방법

이전에 Jetpack Benchmark가 나오기 전에는 어떻게 성능 측정을 진행하였을까요?

바로 아래 그림처럼 System.nanoTime() 함수를 사용하여 시작 시간과 끝 시간 사이의 차이를 기록하며 성능을 측정하였습니다.

이러한 방법은 여러 번 반복 실행시 각각 다르게 측정될 수 있는 변동성, 배경 작업으로 인한 노이즈, 그리고 자원 사용량에 따른 성능 차이 등의 단점들이 있습니다.

즉 성능측정은 일관성이 중요한데 그러한 일관성이 보장이 안되는 것이죠.

똑같은 코드에 대해서 어떤 측정에는 1초 걸리고 또 다른 측정에서는 10초가 걸리는 상황이 발생할 수 있다는 것입니다.

이러한 점을 보안하고자 Jetpack Benchmark를 사용하여 작업의 반복 실행, 배경 작업의 최소화, CPU 및 메모리 최적화 등의 최적화된 기능들과 함께 보다 정확하고 일관된 성능 측정이 가능해집니다.

코드도 훨씬 간략해졌죠. 그 이유는 Benchmark 라이브러리가 내부적으로 반복 실행, 초기화, 종료 등의 작업을 이미 구현 해 놓았기 때문입니다.

Benchmark를 할 때 고려할 점

최적의 결과를 위해 실제 디바이스에서 테스트를 실행하는 것이 좋습니다. 에뮬레이터에서는 정확한 성능 측정이 어려울 수 있습니다. 백그라운드 작업, 네트워크 연결 상태, 배터리 상태 등 외부 요인의 영향을 최소화하여 테스트하는 것이 좋습니다. 측정할 때는 Release 버전과 흡사한 환경으로 설정해주면 좋다고합니다.

즉 benchmark는 실제 릴리스 앱 상태에서 실제 디바이스에서 측정하는 것이 일반적이라고 합니다.

Benchmark Configuration

  • debuggable="false"
  • codeCoverageEnabled="false"
  • Enable Proguard / R8
  • Currently supports library modules

이렇게 하는 이유는 Debug 모드에서 애플리케이션은 여러 추가적인 디버깅 정보와 기능을 포함하고 있습니다. 이는 성능에 추가적인 오버헤드를 발생시킬 수 있습니다.

코드 커버리지는 테스트가 코드의 어느 부분을 실행하는지 측정하는 기능인데요 이 기능 또한 추가적인 성능 오버헤드를 발생시키므로, 성능 측정 시에는 비활성화하는 것이 좋습니다.

Proguard와 R8은 코드 축소, 최적화, 난독화를 수행하는 도구입니다 이 도구들을 사용하면 불필요한 코드가 제거되고, 실행 성능이 향상될 수 있습니다 실제 Release 버전의 앱에서는 이 도구들을 사용하기 때문에, 벤치마크 테스트에서도 이와 유사한 환경을 만들기 위해 활성화하는 것이 좋습니다.

이러한 설정들을 통해 벤치마크 테스트는 실제 사용자들에게 제공될 최종 애플리케이션의 성능에 가까운 환경에서 정확한 성능 측정을 수행할 수 있게 됩니다

구글 I/O 영상에서는 아래 그래프 그림으로 설명합니다.

이 그래프는 debuggable 속성의 값을 true로 설정했을 때와 false로 설정했을 때의 성능 차이를 나타냅니다.

  • 대체로, debuggable=true로 설정했을 때 성능 측정 결과가 더 나쁜 것으로 나타납니다.
  • 이는 디버깅 정보와 기능들이 추가로 포함되어 성능에 오버헤드를 추가하기 때문입니다.
  • 실제 제품 버전의 앱에서는 최적의 성능을 위해 debuggable=false로 설정하는 것이 바람직합니다.

Clock stability

"Ramp" 현상은 CPU의 클록 주파수가 초기에 낮은 상태에서 시작하여 시간에 따라 점진적으로 증가하는 현상을 의미합니다.

Clocks start low, ramp slowly:

  • 작업 시작 초기에는 CPU 클록 속도가 낮습니다. 그리고 시간이 지남에 따라 속도가 점점 증가합니다. 이는 일반적으로 에너지 소비를 최소화하기 위해 이렇게 설계되었습니다.
  • CPU에 부하(작업)가 적용될 때, 클록 속도가 증가합니다. 그러나 이 ramp-up 과정은 즉각적이지 않으며 일정 시간이 걸릴 수 있습니다.
  • 클록 속도의 증가는 바로 이루어지지 않습니다. 약 100ms의 지연이 있을 수 있다고 합니다.

이러한 초기에 CPU 클록 속도를 낮게 유지하는 것은 에너지 소비 측면에서 디바이스 효율적이지만 벤치마크를 할때 치명적으로 작용할 수 있습니다.

따라서 영상에서는 아래를 설명합니다.

Benchmark runs warmup to account for this:

  • 벤치마크는 이 "Ramp" 현상을 고려하여 'warmup' 단계를 실행합니다. 즉, 실제 측정을 시작하기 전에 일정한 부하를 주어 CPU를 '예열' 시키는 과정을 의미합니다

Spins loop for minimum 250ms:

  • 벤치마크는 최소 250ms 동안 루프를 실행하여 CPU의 클록 주파수가 안정화될 때까지 기다립니다. 이렇게 함으로써 측정의 정확성을 높이려는 의도가 있습니다.

Starts measuring once perf stabilizes:

  • CPU의 성능이 안정화되면, 그때부터 실제 성능 측정이 시작됩니다. 이렇게 하여 "Ramp"에 의한 초기의 불규칙한 성능 변동을 제외하고 실제 애플리케이션의 성능을 정확하게 측정하게 됩니다.

Also handles Android Runtime’s Just-In-Time compilation:

  • 벤치마크는 Android Runtime의 JIT(Just-In-Time) 컴파일에 대해서도 처리합니다 즉 이 JIT 과정에서 발생할 수 있는 추가적인 변동성도 고려하여 안정적인 성능 측정을 보장합니다.

Dive

영상에서는 램핑 보다 큰 문제인 Dive에 대해서 언급합니다.

When device gets hot, clocks dive quickly

디바이스가 뜨거워지면 클럭 속도가 빠르게 감소한다고 설명하고 있습니다

이는 대부분의 모바일 디바이스에서 발생하는 현상으로, 디바이스가 과도하게 열을 발생할 때 내부적으로 손상을 방지하고 안정성을 유지하기 위해 자동으로 클럭 속도를 줄이는 메커니즘이 작동하기 때문입니다.

영상에서는 이 Dive의 치명적인 부분을 그래프로 설명합니다

그래프를 통해 볼 수 있는 주요 포인트는 디바이스가 작업을 계속 수행하면서 열이 발생하게 되면, 그로 인해 디바이스의 성능이 저하되고 이로인해 작업을 수행하는 시간이 급격하게 늘어난다는 것을 보여줍니다.

이러한 현상도 디바이스를 보호하기 위해서 필수적인 매커니즘이지만 밴치마크에서 정확성에 치명적입니다.

또한 과열을 방지하기 위해 성능을 자동으로 제한하는 "Thermal throttling" 현상을 Benchamrk에서 제어하는 법을 설명합니다.

세가지 전략에 대해 설명합니다.

1. Lock clocks

  • "Lock clocks"는 CPU 또는 GPU의 클럭 속도를 고정하는 것을 의미합니다. 이를 통해 기기가 일정한 성능을 유지하도록 할 수 있습니다. 과열이나 다른 이유로 성능이 자주 변동될 때, 이 전략을 사용하여 성능을 일정하게 유지할 수 있습니다.
  • 하지만 이방법은 NOT General 하다고 합니다. 이 방법은 루팅된 기기에서만 가능한 방법이기 때문입니다.

2. Sustained perf

  • "Sustained performance"의 약자로, 이는 기기의 성능을 지속적으로 일정한 수준에서 유지하려는 전략을 의미합니다. 이 전략은 기기의 성능을 최대치로 높이지 않고, 안정적인 수준에서 계속 유지하려는 것을 목표로 합니다.
  • 이 방법 또한 NOT General 하다고 합니다. 이 방법은 일부 N+ 기기에서만 가능한 방법이기 때문입니다.

3. Thread.sleep()

이는 프로그래밍에서 사용되는 함수로, 실행 중인 스레드를 일정 시간동안 일시 정지시키는 기능을 합니다. 이를 통해 CPU의 부하를 줄이거나, 기기의 온도를 안정화시킬 수 있습니다.

영상에서는 Thread.sleep이 일반적인 기기에서 사용할 수 있는 일반적인 방법이라고 언급합니다.

이 전략들은 성능 저하나 과열과 같은 문제를 최소화하고 기기의 안정성을 유지하기 위해 사용됩니다.

Foreground

벤치마크 테스트를 최적의 조건에서 실행하기 위한 또 다른 전략으로 'Foreground' 활성화를 언급합니다.

성능 테스트를 할 때 앱을 사용자가 계속 볼 수 있게 (즉, Foreground에서) 실행하는 이유는 여러 가지가 있습니다:

  1. 우선순위: Foreground에서 실행되는 앱은 보통 운영 체제에 의해 높은 우선순위를 갖습니다. 따라서 CPU, 메모리 및 기타 리소스에 더 좋은 접근성을 갖게 됩니다.
  2. 자원 할당: Background에서 실행되는 앱은 리소스 제한을 받을 수 있습니다. 예를 들어, CPU 시간을 적게 할당받거나 메모리가 제한될 수 있습니다. Foreground에서 실행하면 이러한 제한을 최소화할 수 있습니다.
  3. 정확성: Foreground에서 실행하면 다른 앱이나 서비스에 의한 방해를 줄일 수 있어, 테스트 결과의 정확성이 높아집니다.
  4. 배경 프로세스 간섭 최소화: Foreground에서 앱을 실행하면 배경에서 동작하는 다른 앱이나 프로세스로 인한 간섭을 줄일 수 있습니다. 이로 인해 테스트의 일관성이 높아집니다.

Contention

'Contention'은 여러 프로세스나 스레드가 동시에 특정 자원(예: CPU, 메모리)에 액세스하려고 할 때 발생하는 경쟁 상황을 의미합니다.


"Everything is a shared resource"는 모든 자원이 여러 작업 간에 공유되기 때문에 contention이 발생할 수 있다는 것을 나타냅니다.

위에 그림을 보시면 특정 Loop에서 BG WORK(다른 작업이나 프로세스에 의한 BackGround Work)가 발생하여 시간 지연이 발생하는 것을 보여줍니다.

이러한 특정 Loop에서 Contention으로 인한 시간 지연을 해결하기 위한 전력으로 벤치마크를 여러 번 수행한 후 결과를 한 번만 보고하는 것이 좋다고 합니다.

"Most contention is for a short duration"는 대부분의 contention이 잠깐 발생한다는 것을 의미합니다. 이로 인해 특정 Loop는 영향을 받지만 특정 Loop에서는 Contention에 노출이되지않습니다.

따라서 Iteration을 여러번으로 주는 것이 좋고 분석할 때는 "Report and track minimum, not average"을 하라고 합니다.

이 말은 평균 대신 최소값을 보고하고 추적해야 한다는 것을 의미합니다. 이는 contention으로 인한 극단적인 성능 저하를 반영하기 위한 것입니다

Benchmark에서는 평균값이 아니라 중앙값을 사용하던데 이러한 이유 때문이라고 생각이 듭니다.

마무리


전통적인 벤치마킹은 여러 변수, 예를 들면 Clock stability와 Background interference 등을 고려해야하기 때문에 정확한 성능 측정이 복잡하고 어려움이 많았다고 합니다.


이러한 복잡성을 해결하기 위해 Jetpack Benchmark가 개발되었습니다. 이 도구는 다음과 같은 주요 특징을 갖고 있습니다:

  • Simple API: 'Jetpack Benchmark'는 사용하기 쉬운 API를 제공
  • Bundled with lessons we’ve learned: 과거의 경험과 교훈을 기반으로 벤치마킹의 일반적인 문제점들을 피하고 더욱 정확한 결과를 얻을 수 있게 되었다고 합니다.

2019 구글 I/O Benchmark 설명 및 공식 문서를 참고하여 작성하였습니다!!

Benchmark를 적용하는 자세한 방법은 공식문서를 참고해주세요.
최신 release 소식은 Link에서 확인할 수 있습니다.

다음 글은 실제 제 프로젝트에 Benchmark 적용과 Baseline-profile에 대해 준비하겠습니다!

profile
Android Developer

0개의 댓글