출시일:
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 스케줄러 동작](https://developer.android.com/static/games/optimize/adpf/images/adpf_linux_sched_behavior.png?authuser=1&hl=ko)
PerformanceHint API는 DVFS 지연 시간 이상을 추상화함
![ADPF는 DVFS 지연 시간보다 더 많은 추상화](https://developer.android.com/static/games/optimize/adpf/images/adpf_dvfs_latency.png?authuser=1&hl=ko)
- </ph>
- 작업을 특정 CPU에서 실행해야 하는 경우 PerformanceHint API는 광고주를 대신하여 결정을 내리게 됩니다.
- 따라서 어피니티를 사용할 필요가 없습니다.
- 기기에는 다양한 토폴로지가 함께 제공됩니다. 전력 및 열 특성은 앱 개발자에게 공개하기에는 너무 다양합니다.
- 실행 중인 기본 시스템에 대해 어떤 가정도 할 수 없습니다.
해결 방법
ADPF는 PerformanceHintManager
클래스를 통해 게임에서 CPU 클럭 속도와 관련된 성능 힌트를 Android에 보낼 수 있습니다.
코어 유형입니다 그런 다음, OS는 SoC 및 기기 열 솔루션을 기반으로 힌트를 가장 잘 사용할 수 있는 방법을 결정할 수 있습니다. 앱이 이 API와 함께 열 상태 모니터링을 사용하는 경우 제한이 발생할 수 있는 비지 루프와 그 외 다른 코딩 기법을 사용하는 대신 OS에 더 많은 정보가 담긴 힌트를 제공할 수 있습니다.
게임에서 성능 힌트를 사용하는 방법은 다음과 같습니다.
- 유사하게 동작하는 키 스레드의 힌트 세션을 만듭니다. 예:
- 렌더링 스레드 및 그 종속 항목이 하나의 세션을 받음
<ph type="x-smartling-placeholder">
- </ph>
- Cocos에서는 기본 엔진 스레드와 렌더링 스레드가 하나의 세션
- Unity에서 Adaptive Performance Android Provider 플러그인을 통합합니다.
- Unreal에서 Unreal Adaptive Performance 플러그인을 통합하고 다양한 품질 수준을 지원하는 확장성 옵션
- IO 스레드가 다른 세션을 받음
- 오디오 스레드가 세 번째 세션을 받음
- 렌더링 스레드 및 그 종속 항목이 하나의 세션을 받음
<ph type="x-smartling-placeholder">
- 게임은 이 작업을 초기에 해야 합니다. 세션에서 시스템 리소스를 더 많이 필요로 하는 시점으로부터 최소 2밀리초(4밀리초를 넘는 것이 권장됨) 전에 해야 합니다.
- 각 힌트 세션에서 각 세션을 실행하는 데 필요한 시간을 예측합니다. 일반적인 지속 시간은 프레임 간격과 같지만 앱에서 워크로드가 프레임 간에 크게 달라지지 않는 경우 간격이 짧아집니다.
다음은 이론을 실천하는 방법입니다.
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);