성능 힌트 API

출시일:

Android 12 (API 수준 31) - Performance Hint API

Android 13 (API 수준 33) - NDK API의 성능 힌트 관리자

(미리보기) Android 15 (DP1) - reportActualWorkDuration()

CPU 성능 힌트를 사용하면 게임이 동적 CPU 성능에 영향을 줄 수 있습니다. 더욱 효과적으로 제어할 수 있습니다 대부분의 기기에서 Android는 워크로드의 CPU 클록 속도와 코어 유형을 직접 선택할 수 있습니다. 워크로드가 더 많은 CPU 리소스를 사용하면 클록 속도가 빨라지고 결국 더 큰 코어로 이동합니다 워크로드가 Android는 리소스 할당을 줄입니다. ADPF를 사용하면 애플리케이션은 실적 및 기한에 관한 추가 신호를 보낼 수 있습니다. 이 시스템이 보다 공격적으로 증가하여 (성능 개선) 신속하게 클록을 잠글 수 있습니다 (전력 사용량 절약).

클럭 속도

Android 기기가 CPU 클록 속도를 동적으로 조정할 때 주파수는 코드 성능을 변경할 수 있습니다. 동적 시계를 처리하는 코드 설계 성능을 극대화하고 열을 안전하게 유지하는 데 중요합니다. 전력을 효율적으로 사용합니다. CPU 주파수는 직접 할당할 수 없습니다. 삽입해야 합니다 따라서 앱이 더 높은 하드웨어에서 실행하려고 하는 일반적인 방법은 CPU 클록 속도는 백그라운드 스레드에서 사용 중인 루프를 실행하여 워크로드가 좀 더 까다로운 것 같습니다. 이것은 전력을 낭비하고 실제로 추가 CPU를 사용하지 않고 기기의 열 부하를 리소스를 배포합니다 CPU PerformanceHint API는 이 문제를 해결하기 위해 설계되었습니다. 작성자: 시스템에 실제 작업 기간과 목표 작업 기간을 알립니다. Android가 앱의 CPU 요구사항을 개략적으로 파악하고 효율적으로 배포할 수 있습니다 이를 통해 효율적인 전력으로 최적의 성능을 얻을 수 있습니다. 있습니다.

코어 유형

게임이 실행되는 CPU 코어 유형은 또 다른 중요한 성능 요소입니다. Android 기기는 최근 워크로드 동작에 따라 스레드에 동적으로 할당된 CPU 코어를 변경하기도 합니다. CPU 코어 할당은 여러 코어 유형을 사용하는 SoC에서 훨씬 더 복잡합니다. 이러한 기기 중 일부에서는 더 큰 코어를 온도로 인해 지속할 수 없는 상태로 전환하지 않고 잠시 동안만 사용할 수 있습니다.

게임에서 CPU 코어 어피니티를 설정하려고 하면 안 되는 이유는 다음과 같습니다.

  • 워크로드에 가장 적합한 코어 유형은 기기 모델에 따라 다릅니다.
  • 더 큰 코어를 실행하는 지속 가능성은 SoC 및 각 기기 모델에서 제공하는 다양한 열 솔루션에 따라 다릅니다.
  • 이러한 열 상태에 미치는 환경적 영향은 코어 선택을 더 복잡하게 할 수 있습니다. 예를 들어 날씨나 휴대전화 케이스로 인해 기기의 열 상태가 바뀔 수 있습니다.
  • 코어 선택은 추가 성능 및 열 기능을 갖춘 새 기기를 수용할 수 없습니다. 따라서 기기는 게임의 프로세서 어피니티를 무시하는 경우가 많습니다.

기본 Linux 스케줄러 동작의 예

Linux 스케줄러 동작
그림 1. 거버너는 CPU 주파수를 높이거나 낮추는 데 최대 200ms가 걸릴 수 있습니다. ADPF는 동적 전압 및 주파수 확장 시스템 (DVFS)과 함께 작동하여 와트당 최상의 성능을 제공합니다.
를 통해 개인정보처리방침을 정의할 수 있습니다.

PerformanceHint API는 DVFS 지연 시간 이상을 추상화함

ADPF는 DVFS 지연 시간보다 더 많은 추상화
그림 2. ADPF는 사용자를 대신해 최선의 결정을 내리는 방법을 알고 있습니다.
를 통해 개인정보처리방침을 정의할 수 있습니다. <ph type="x-smartling-placeholder">
    </ph>
  • 작업을 특정 CPU에서 실행해야 하는 경우 PerformanceHint API는 광고주를 대신하여 결정을 내리게 됩니다.
  • 따라서 어피니티를 사용할 필요가 없습니다.
  • 기기에는 다양한 토폴로지가 함께 제공됩니다. 전력 및 열 특성은 앱 개발자에게 공개하기에는 너무 다양합니다.
  • 실행 중인 기본 시스템에 대해 어떤 가정도 할 수 없습니다.

해결 방법

ADPF는 PerformanceHintManager 클래스를 통해 게임에서 CPU 클럭 속도와 관련된 성능 힌트를 Android에 보낼 수 있습니다. 코어 유형입니다 그런 다음, OS는 SoC 및 기기 열 솔루션을 기반으로 힌트를 가장 잘 사용할 수 있는 방법을 결정할 수 있습니다. 앱이 이 API와 함께 열 상태 모니터링을 사용하는 경우 제한이 발생할 수 있는 비지 루프와 그 외 다른 코딩 기법을 사용하는 대신 OS에 더 많은 정보가 담긴 힌트를 제공할 수 있습니다.

게임에서 성능 힌트를 사용하는 방법은 다음과 같습니다.

  1. 유사하게 동작하는 키 스레드의 힌트 세션을 만듭니다. 예:
    • 렌더링 스레드 및 그 종속 항목이 하나의 세션을 받음 <ph type="x-smartling-placeholder">
        </ph>
      1. Cocos에서는 기본 엔진 스레드와 렌더링 스레드가 하나의 세션
      2. Unity에서 Adaptive Performance Android Provider 플러그인을 통합합니다.
      3. Unreal에서 Unreal Adaptive Performance 플러그인을 통합하고 다양한 품질 수준을 지원하는 확장성 옵션
    • IO 스레드가 다른 세션을 받음
    • 오디오 스레드가 세 번째 세션을 받음
  2. 게임은 이 작업을 초기에 해야 합니다. 세션에서 시스템 리소스를 더 많이 필요로 하는 시점으로부터 최소 2밀리초(4밀리초를 넘는 것이 권장됨) 전에 해야 합니다.
  3. 각 힌트 세션에서 각 세션을 실행하는 데 필요한 시간을 예측합니다. 일반적인 지속 시간은 프레임 간격과 같지만 앱에서 워크로드가 프레임 간에 크게 달라지지 않는 경우 간격이 짧아집니다.

다음은 이론을 실천하는 방법입니다.

PerformanceHintManager 및 createHintSession 초기화

시스템 서비스를 사용하여 관리자를 가져오고 스레드의 힌트 세션 만들기 동일한 워크로드에서 작동하는 스레드 그룹이나 스레드 그룹을 생성할 수 있습니다

C++

int32_t tids[1];
tids[0] = gettid();
int64_t target_fps_nanos = getFpsNanos();
APerformanceHintManager* hint_manager = APerformanceHint_getManager();
APerformanceHintSession* hint_session =
  APerformanceHint_createSession(hint_manager, tids, 1, target_fps_nanos);

자바

int[] tids = {
  android.os.Process.myTid()
};
long targetFpsNanos = getFpsNanos();
PerformanceHintManager performanceHintManager =
  (PerformanceHintManager) this.getSystemService(Context.PERFORMANCE_HINT_SERVICE);
PerformanceHintManager.Session hintSession =
  performanceHintManager.createHintSession(tids, targetFpsNanos);

필요한 경우 스레드 설정

출시일:

Android 11 (API 수준 34)

setThreads 사용 다른 스레드가 있을 때 PerformanceHintManager.Session의 함수 나중에 추가해야 합니다. 예를 들어 물리 스레드를 만들면 세션에 추가해야 하는 경우 이 setThreads API를 사용하면 됩니다.

C++

auto tids = thread_ids.data();
std::size_t size = thread_ids_.size();
APerformanceHint_setThreads(hint_session, tids, size);

자바

int[] tids = new int[3];

// add all your thread IDs. Remember to use android.os.Process.myTid() as that
// is the linux native thread-id.
// Thread.currentThread().getId() will not work because it is jvm's thread-id.
hintSession.setThreads(tids);

더 낮은 API 레벨을 대상으로 하는 경우, 세션을 소멸시키고 스레드 ID를 변경해야 할 때마다 새 세션을 다시 만들어야 합니다.

실제 작업 시간 보고

작업을 완료하는 데 필요한 실제 소요 시간 추적(나노초) 및 보고 시스템에 전송합니다. 예를 들어 렌더링 스레드용이며 모든 프레임에서 호출하세요.

실제 시간을 안정적으로 가져오려면 다음을 사용하세요.

C++

clock_gettime(CLOCK_MONOTONIC, &clock); // if you prefer "C" way from <time.h>
// or
std::chrono::high_resolution_clock::now(); // if you prefer "C++" way from <chrono>

자바

System.nanoTime();

예를 들면 다음과 같습니다.

C++

// All timings should be from `std::chrono::steady_clock` or `clock_gettime(CLOCK_MONOTONIC, ...)`
auto start_time = std::chrono::high_resolution_clock::now();

// do work

auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count();
int64_t actual_duration = static_cast<int64_t>(duration);

APerformanceHint_reportActualWorkDuration(hint_session, actual_duration);

자바

long startTime = System.nanoTime();

// do work

long endTime = System.nanoTime();
long duration = endTime - startTime;

hintSession.reportActualWorkDuration(duration);

필요한 경우 목표 작업 시간 업데이트

목표 작업 기간이 변경될 때마다, 예를 들어 플레이어가 다른 목표 fps를 지정하려면 updateTargetWorkDuration를 호출합니다. 시스템이 이 사실을 알 수 있도록 하기 위해 OS가 주어진 조건에 따라 리소스를 조정할 수 있도록 새 타겟으로 전달됩니다. 모든 프레임에서 호출할 필요는 없으며 타겟 지속 시간이 변경되면 이를 호출합니다.

C++

APerformanceHint_updateTargetWorkDuration(hint_session, target_duration);

자바

hintSession.updateTargetWorkDuration(targetDuration);