CPU 성능 힌트를 사용하면 게임이 동적 CPU 성능 동작에 영향을 주어 요구사항에 더 잘 맞출 수 있습니다. 대부분의 기기에서 Android는 이전 요구사항에 따라 CPU 클록 속도와 워크로드의 코어 유형을 동적으로 조정합니다.
워크로드가 더 많은 CPU 리소스를 사용하면 클록 속도가 빨라지고 결국 워크로드는 더 큰 코어로 이동합니다. 워크로드에서 더 적은 리소스를 사용하면 Android는 리소스 할당을 줄입니다. ADPF를 사용하면 애플리케이션이나 게임에서 성능 및 기한에 관한 추가 신호를 보낼 수 있습니다. 이를 통해 시스템이 더 적극적으로 증가하여 성능이 개선되고 워크로드가 완료되면 클럭이 빠르게 낮아져 전력 사용량이 절약됩니다.
클럭 속도
Android 기기에서 CPU 클록 속도를 동적으로 조정하면 주파수가 코드의 성능에 영향을 미칠 수 있습니다. 동적 클록 속도를 처리하는 코드를 설계하는 것은 성능을 극대화하고 열 상태를 안전하게 유지하며 전력을 효율적으로 사용하는 데 중요합니다. 앱 코드에서 CPU 주파수를 직접 할당할 수 없습니다. 따라서, 앱이 더 높은 CPU 클록 속도에서 실행되도록 하는 일반적인 방법은 백그라운드 스레드에 비지 루프를 실행하여 워크로드가 더 많이 필요한 것처럼 보이는 것입니다. 이는 앱이 실제로 추가 리소스를 사용하지 않을 때 전력이 낭비되고 기기의 열 부하가 증가하므로 좋지 않은 방법입니다. CPU PerformanceHint API는 이 문제를 해결하기 위해 설계되었습니다. 실제 작업 시간과 타겟 작업 시간을 시스템에 알리면 Android에서 앱의 CPU 요구사항을 개략적으로 파악하고 리소스를 효율적으로 할당할 수 있습니다. 이렇게 하면 효율적인 전력 소비 수준에서 최적의 성능을 낼 수 있습니다.
코어 유형
게임이 실행되는 CPU 코어 유형은 또 다른 중요한 성능 요소입니다. Android 기기는 최근 워크로드 동작에 따라 스레드에 동적으로 할당된 CPU 코어를 변경하기도 합니다. CPU 코어 할당은 여러 코어 유형을 사용하는 SoC에서 훨씬 더 복잡합니다. 이러한 기기 중 일부에서는 더 큰 코어를 온도로 인해 지속할 수 없는 상태로 전환하지 않고 잠시 동안만 사용할 수 있습니다.
게임에서 CPU 코어 어피니티를 설정하려고 하면 안 되는 이유는 다음과 같습니다.
워크로드에 가장 적합한 코어 유형은 기기 모델에 따라 다릅니다.
더 큰 코어를 실행하는 지속 가능성은 SoC 및 각 기기 모델에서 제공하는 다양한 열 솔루션에 따라 다릅니다.
이러한 열 상태에 미치는 환경적 영향은 코어 선택을 더 복잡하게 할 수 있습니다. 예를 들어 날씨나 휴대전화 케이스로 인해 기기의 열 상태가 바뀔 수 있습니다.
코어 선택은 추가 성능 및 열 기능을 갖춘 새 기기를 수용할 수 없습니다. 따라서 기기는 게임의 프로세서 어피니티를 무시하는 경우가 많습니다.
기본 Linux 스케줄러 동작의 예
그림 1. 거버너가 CPU 주파수를 높이거나 낮추는 데 약 200ms가 걸릴 수 있습니다. ADPF는 동적 전압 및 주파수 조정 시스템 (DVFS)과 함께 작동하여 와트당 최고의 성능을 제공합니다.
PerformanceHint API는 DVFS 지연 시간 이상을 추상화함
그림 2. ADPF는 사용자를 대신하여 최적의 결정을 내리는 방법을 알고 있습니다.
작업을 특정 CPU에서 실행해야 하는 경우 PerformanceHint API는 사용자를 대신하여 결정을 내리는 방법을 알고 있습니다.
따라서 어피니티를 사용하지 않아도 됩니다.
기기는 다양한 토폴로지로 제공됩니다. 전력 및 열 특성은 앱 개발자에게 노출하기에는 너무 다양합니다.
실행 중인 기본 시스템에 대해 가정할 수 없습니다.
해결 방법
ADPF는 PerformanceHintManager 클래스를 제공하므로 게임에서 CPU 클럭 속도와 코어 유형의 성능 힌트를 Android에 보낼 수 있습니다. 그런 다음, OS는 SoC 및 기기 열 솔루션을 기반으로 힌트를 가장 잘 사용할 수 있는 방법을 결정할 수 있습니다. 앱이 이 API와 함께 열 상태 모니터링을 사용하는 경우 제한이 발생할 수 있는 비지 루프와 그 외 다른 코딩 기법을 사용하는 대신 OS에 더 많은 정보가 담긴 힌트를 제공할 수 있습니다.
int[]tids=newint[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>// orstd::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, ...)`autostart_time=std::chrono::high_resolution_clock::now();// do workautoend_time=std::chrono::high_resolution_clock::now();autoduration=std::chrono::duration_cast<std::chrono::nanoseconds>(end_time-start_time).count();int64_tactual_duration=static_cast<int64_t>(duration);APerformanceHint_reportActualWorkDuration(hint_session,actual_duration);
자바
longstartTime=System.nanoTime();// do worklongendTime=System.nanoTime();longduration=endTime-startTime;hintSession.reportActualWorkDuration(duration);
필요한 경우 타겟 작업 기간 업데이트
타겟 작업 기간이 변경될 때마다(예: 플레이어가 다른 타겟 FPS를 선택하는 경우) updateTargetWorkDuration 메서드를 호출하여 시스템에 알립니다. 그러면 OS가 새 타겟에 따라 리소스를 조정할 수 있습니다. 프레임마다 호출할 필요는 없으며 타겟 지속 시간이 변경될 때만 호출하면 됩니다.
이 페이지에 나와 있는 콘텐츠와 코드 샘플에는 콘텐츠 라이선스에서 설명하는 라이선스가 적용됩니다. 자바 및 OpenJDK는 Oracle 및 Oracle 계열사의 상표 또는 등록 상표입니다.
최종 업데이트: 2025-07-26(UTC)
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["필요한 정보가 없음","missingTheInformationINeed","thumb-down"],["너무 복잡함/단계 수가 너무 많음","tooComplicatedTooManySteps","thumb-down"],["오래됨","outOfDate","thumb-down"],["번역 문제","translationIssue","thumb-down"],["샘플/코드 문제","samplesCodeIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 2025-07-26(UTC)"],[],[],null,["# Performance Hint API\n\n**Released**:\n\nAndroid 12 (API Level 31) - [Performance Hint API](/reference/android/os/PerformanceHintManager)\n\nAndroid 13 (API Level 33) - [Performance Hint Manager in the NDK API](/ndk/reference/group/a-performance-hint)\n\n(Preview) Android 15 (DP1) - [`reportActualWorkDuration()`](/reference/android/os/PerformanceHintManager.Session#reportActualWorkDuration(android.os.WorkDuration))\n\nWith CPU performance hints, a game can influence dynamic CPU performance\nbehavior to better match its needs. On most devices, Android dynamically adjusts\nthe CPU clock speed and core type for a workload based on the previous demands.\nIf a workload uses more CPU resources, the clock speed is increased and the\nworkload is eventually moved to a larger core. If the workload uses less\nresources, then Android lowers resource allocation. With ADPF, the application\nor game can send an additional signal about its performance and deadlines. This\nhelps the system ramp up more aggressively (improving performance) and lower the\nclocks quickly when the workload is complete (saving power usage).\n\nClock speed\n-----------\n\nWhen Android devices dynamically adjust their CPU clock speed, the frequency can\nchange the performance of your code. Designing code that addresses dynamic clock\nspeeds is important for maximizing performance, maintaining a safe thermal\nstate, and using power efficiently. You cannot directly assign CPU frequencies\nin your app code. As a result, a common way for apps to attempt to run at higher\nCPU clock speeds is to run a busy loop in a background thread so the workload\nseems more demanding. This is a bad practice as it wastes power and increases\nthe thermal load on the device when the app isn't actually using the additional\nresources. The CPU `PerformanceHint` API is designed to address this problem. By\nletting the system know the actual work duration and the target work duration,\nAndroid will be able to get an overview of the app's CPU needs and allocate\nresources efficiently. This will lead to optimum performance at efficient power\nconsumption level.\n\nCore types\n----------\n\nThe CPU core types that your game runs on are another important performance\nfactor. Android devices often change the CPU core assigned to a thread\ndynamically based on recent workload behavior. CPU core assignment is even more\ncomplex on SoCs with multiple core types. On some of these devices, the larger\ncores can only be used briefly without going into a thermally unsustainable\nstate.\n\nYour game shouldn't try to set the CPU core affinity for the following reasons:\n\n- The best core type for a workload varies by device model.\n- The sustainability of running larger cores varies by SoC and by the various thermal solutions provided by each device model.\n- The environmental impact on the thermal state can further complicate core choice. For example, the weather or a phone case can change the thermal state of a device.\n- Core selection can't accommodate new devices with additional performance and thermal capabilities. As a result, devices often ignore a game's processor affinity.\n\n### Example of default Linux scheduler behavior\n\n**Figure 1.** Governor can take \\~200ms to ramp up or down CPU frequency. ADPF works with the Dynamic Voltage and Frequency Scaling system (DVFS) to provide best performance per watt\n\n### PerformanceHint API abstracts more than DVFS latencies\n\n**Figure 2.** ADPF knows how to make the best decision on your behalf\n\n- If the tasks need to run on a specific CPU, PerformanceHint API knows how to make that decision on your behalf.\n- Therefore, you need not use affinity.\n- Devices come with various topologies; Power and thermal characteristics are too varied to be exposed to app developer.\n- You can't make any assumptions about the underlying system you're running on.\n\nSolution\n--------\n\nADPF provides the [`PerformanceHintManager`](/reference/android/os/PerformanceHintManager)\nclass so games can send performance hints to Android for CPU clock speed and\ncore type. The OS can then decide how to best use the hints based on the SoC and\nthermal solution of the device. If your app uses this API along with thermal\nstate monitoring, it can provide more informed hints to the OS instead of using\nbusy loops and other coding techniques that can cause throttling.\n\nThis is how a game uses performance hints:\n\n1. [Create hint sessions](/reference/android/os/PerformanceHintManager#createHintSession(int%5B%5D,%20long)) for key threads that behave similarly. For example:\n - Rendering thread and its dependencies get one session\n 1. In Cocos, the main engine thread and render thread gets [one\n session](https://github.com/cocos/cocos-engine/blob/v3.8.5-dianchu/native/cocos/base/threading/MessageQueue.cpp)\n 2. In Unity, integrate [Adaptive Performance Android Provider plugin](https://docs.unity3d.com/Packages/com.unity.adaptiveperformance.google.android@1.2/manual/index.html)\n 3. In Unreal, integrate Unreal Adaptive Performance plugin and use [Scalability options](https://docs.unrealengine.com/4.27/en-US/TestingAndOptimization/PerformanceAndProfiling/Scalability/ScalabilityReference/) to support multiple quality levels\n - IO threads get another session\n - Audio threads get a third session\n2. The game should do this early, at least 2ms and preferably more than 4ms before a session needs increased system resources.\n3. In each hint session, predict the duration needed for each session to run. The typical duration is equivalent to a frame interval, but the app can use a shorter interval if the workload does not vary significantly across frames.\n\nHere is how to put the theory into practice:\n\n### Initialize PerformanceHintManager and createHintSession\n\nGet the manager using system service and create a hint session for your thread\nor thread group working on the same workload. \n\n### C++\n\n int32_t tids[1];\n tids[0] = gettid();\n int64_t target_fps_nanos = getFpsNanos();\n APerformanceHintManager* hint_manager = APerformanceHint_getManager();\n APerformanceHintSession* hint_session =\n APerformanceHint_createSession(hint_manager, tids, 1, target_fps_nanos);\n\n### Java\n\n int[] tids = {\n android.os.Process.myTid()\n };\n long targetFpsNanos = getFpsNanos();\n PerformanceHintManager performanceHintManager =\n (PerformanceHintManager) this.getSystemService(Context.PERFORMANCE_HINT_SERVICE);\n PerformanceHintManager.Session hintSession =\n performanceHintManager.createHintSession(tids, targetFpsNanos);\n\n### Set threads if necessary\n\n**Released**:\n\nAndroid 11 (API Level 34)\n\nUse the [`setThreads`](/reference/android/os/PerformanceHintManager.Session#setThreads(int%5B%5D))\nfunction of the `PerformanceHintManager.Session` when you have other threads\nthat need to be added later. For example, if you create your physics thread\nlater and need to add it to the session, you can use this `setThreads` API. \n\n### C++\n\n auto tids = thread_ids.data();\n std::size_t size = thread_ids_.size();\n APerformanceHint_setThreads(hint_session, tids, size);\n\n### Java\n\n int[] tids = new int[3];\n\n // add all your thread IDs. Remember to use android.os.Process.myTid() as that\n // is the linux native thread-id.\n // Thread.currentThread().getId() will not work because it is jvm's thread-id.\n hintSession.setThreads(tids);\n\nIf you are targeting lower API Levels, you will need to destroy the session and\nre-create a new session every time you need to change the thread IDs.\n\n### Report Actual Work Duration\n\nTrack the actual duration needed to complete the work in nanoseconds and report\nit to the system upon completion of the work on every cycle. For example, if\nthis is for your rendering threads, call this on every frame.\n\nTo get the actual time reliably, use: \n\n### C++\n\n clock_gettime(CLOCK_MONOTONIC, &clock); // if you prefer \"C\" way from \u003ctime.h\u003e\n // or\n std::chrono::high_resolution_clock::now(); // if you prefer \"C++\" way from \u003cchrono\u003e\n\n### Java\n\n System.nanoTime();\n\nFor example: \n\n### C++\n\n // All timings should be from `std::chrono::steady_clock` or `clock_gettime(CLOCK_MONOTONIC, ...)`\n auto start_time = std::chrono::high_resolution_clock::now();\n\n // do work\n\n auto end_time = std::chrono::high_resolution_clock::now();\n auto duration = std::chrono::duration_cast\u003cstd::chrono::nanoseconds\u003e(end_time - start_time).count();\n int64_t actual_duration = static_cast\u003cint64_t\u003e(duration);\n\n APerformanceHint_reportActualWorkDuration(hint_session, actual_duration);\n\n### Java\n\n long startTime = System.nanoTime();\n\n // do work\n\n long endTime = System.nanoTime();\n long duration = endTime - startTime;\n\n hintSession.reportActualWorkDuration(duration);\n\n### Update Target Work Duration when necessary\n\nWhenever your target work duration changes, for example if the player chooses a\ndifferent target fps, call the [`updateTargetWorkDuration`](/reference/android/os/PerformanceHintManager.Session#updateTargetWorkDuration(long))\nmethod to let the system know so that the OS can adjust the resources according\nto the new target. You don't have to call it on every frame and only need to\ncall it when the target duration changes. \n\n### C++\n\n APerformanceHint_updateTargetWorkDuration(hint_session, target_duration);\n\n### Java\n\n hintSession.updateTargetWorkDuration(targetDuration);"]]