Yayın tarihi:
Android 12 (API düzeyi 31) - Performance Hint API
Android 13 (API düzeyi 33) - NDK API'sinde Performans İpucu Yöneticisi
(Önizleme) Android 15 (DP1) - reportActualWorkDuration()
CPU performans ipuçları sayesinde bir oyun, dinamik CPU performans davranışını kendi ihtiyaçlarına daha iyi uyacak şekilde etkileyebilir. Çoğu cihazda Android, önceki taleplere göre bir iş yükü için CPU saat hızını ve çekirdek türünü dinamik olarak ayarlar. Bir iş yükü daha fazla CPU kaynağı kullanıyorsa saat hızı artırılır ve iş yükü sonunda daha büyük bir çekirdeğe taşınır. İş yükü daha az kaynak kullanıyorsa Android, kaynak ayırmayı azaltır. ADPF ile uygulama veya oyun, performansı ve son tarihler hakkında ek bir sinyal gönderebilir. Bu, sistemin daha agresif bir şekilde hızlanmasına (performansı artırır) ve iş yükü tamamlandığında saatleri hızlı bir şekilde düşürmesine (güç kullanımını azaltır) yardımcı olur.
Saat hızı
Android cihazlar CPU saat hızını dinamik olarak ayarladığında frekans, kodunuzun performansını değiştirebilir. Dinamik saat hızlarını ele alan kod tasarlamak; performansı en üst düzeye çıkarmak, güvenli bir termal durumu korumak ve gücü verimli kullanmak için önemlidir. Uygulama kodunuzda CPU frekanslarını doğrudan atayamazsınız. Bu nedenle, uygulamaların daha yüksek CPU saat hızlarında çalışmaya çalışmasının yaygın bir yolu, iş yükünün daha zorlu görünmesi için arka plan iş parçacığında yoğun bir döngü çalıştırmaktır. Bu, kötü bir uygulamadır. Uygulama ek kaynakları kullanmadığı halde güç israfına yol açar ve cihazdaki termal yükü artırır. CPU PerformanceHint
API, bu sorunu gidermek için tasarlanmıştır. Sisteme gerçek çalışma süresini ve hedef çalışma süresini bildirerek Android'in uygulamanın CPU ihtiyaçlarına genel bir bakış elde etmesini ve kaynakları verimli bir şekilde tahsis etmesini sağlayabilirsiniz. Bu, verimli güç tüketimi düzeyinde optimum performans elde edilmesini sağlar.
Temel türler
Oyununuzun üzerinde çalıştığı CPU çekirdek türleri de önemli bir performans faktörüdür. Android cihazlar, bir işleme atanan CPU çekirdeğini son iş yükü davranışına göre dinamik olarak değiştirir. CPU çekirdek ataması, birden fazla çekirdek türüne sahip SoC'lerde daha da karmaşıktır. Bu cihazlardan bazılarında, daha büyük çekirdekler termal olarak sürdürülebilir olmayan bir duruma girmeden yalnızca kısa süre kullanılabilir.
Oyununuz aşağıdaki nedenlerden dolayı CPU çekirdek yakınlığını ayarlamaya çalışmamalıdır:
- Bir iş yükü için en iyi çekirdek türü, cihaz modeline göre değişir.
- Daha büyük çekirdekleri çalıştırmanın sürdürülebilirliği, SoC'ye ve her cihaz modelinin sağladığı çeşitli termal çözümlere göre değişir.
- Termal durumun çevre üzerindeki etkisi, çekirdek seçimi işlemini daha da karmaşık hale getirebilir. Örneğin, hava durumu veya telefon kılıfı, cihazın termal durumunu değiştirebilir.
- Çekirdek seçimi, ek performans ve termal özelliklere sahip yeni cihazları desteklemiyor. Bu nedenle, cihazlar genellikle bir oyunun işlemci yakınlığını göz ardı eder.
Varsayılan Linux zamanlayıcı davranışına örnek

PerformanceHint API, DVFS gecikmelerinden daha fazlasını soyutlar

- Görevlerin belirli bir CPU'da çalışması gerekiyorsa PerformanceHint API, bu kararı sizin adınıza nasıl vereceğini bilir.
- Bu nedenle, yakın ilgi alanı kullanmanız gerekmez.
- Cihazlar çeşitli topolojilerle gelir. Güç ve termal özellikler, uygulama geliştiricilere sunulmayacak kadar çeşitlidir.
- Çalıştırdığınız temel sistem hakkında herhangi bir varsayımda bulunamazsınız.
Çözüm
ADPF, oyunların CPU saat hızı ve çekirdek türü için Android'e performans ipuçları gönderebilmesi amacıyla PerformanceHintManager
sınıfını sağlar. Ardından işletim sistemi, cihazın SoC'sine ve termal çözümüne göre ipuçlarını en iyi şekilde nasıl kullanacağına karar verebilir. Uygulamanız bu API'yi termal durum izlemeyle birlikte kullanıyorsa yoğun döngüler ve sınırlamaya neden olabilecek diğer kodlama tekniklerini kullanmak yerine işletim sistemine daha bilinçli ipuçları sağlayabilir.
Oyunlar performans ipuçlarını şu şekilde kullanır:
- Benzer şekilde davranan önemli iş parçacıkları için ipucu oturumları oluşturun. Örneğin:
- Oluşturma iş parçacığı ve bağımlılıkları tek bir oturum alır.
- Cocos'ta ana motor iş parçacığı ve oluşturma iş parçacığı bir oturum alır.
- Unity'de Adaptive Performance Android Provider eklentisini entegre edin.
- Unreal'da Unreal Adaptive Performance eklentisini entegre edin ve birden fazla kalite düzeyini desteklemek için ölçeklenebilirlik seçeneklerini kullanın.
- G/Ç iş parçacıkları başka bir oturum alır
- Sesli ileti dizileri üçüncü bir oturum alır
- Oluşturma iş parçacığı ve bağımlılıkları tek bir oturum alır.
- Oyun bunu erken yapmalıdır. Bir oturumun sistem kaynaklarının artırılması gerekmeden önce bu işlem en az 2 ms, tercihen 4 ms'den uzun sürmelidir.
- Her ipucu oturumunda, her oturumun çalışması için gereken süreyi tahmin edin. Normal süre, bir kare aralığına eşittir ancak iş yükü kareler arasında önemli ölçüde değişmiyorsa uygulama daha kısa bir aralık kullanabilir.
Teoriyi pratiğe geçirmek için yapmanız gerekenler:
Initialize PerformanceHintManager and createHintSession
Sistem hizmetini kullanarak yöneticiden bilgi alın ve aynı iş yükü üzerinde çalışan iş parçacığınız veya iş parçacığı grubunuz için ipucu oturumu oluşturun.
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);
Java
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);
Gerekirse ileti dizilerini ayarlayın
Yayın tarihi:
Android 11 (API düzeyi 34)
Daha sonra eklenmesi gereken başka ileti dizileriniz olduğunda PerformanceHintManager.Session
'ün setThreads
işlevini kullanın. Örneğin, fizik iş parçacığınızı daha sonra oluşturursanız ve bunu oturuma eklemeniz gerekirse bu setThreads
API'yi kullanabilirsiniz.
C++
auto tids = thread_ids.data();
std::size_t size = thread_ids_.size();
APerformanceHint_setThreads(hint_session, tids, size);
Java
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);
Daha düşük API düzeylerini hedefliyorsanız oturumu sonlandırmanız ve iş parçacığı kimliklerini her değiştirmeniz gerektiğinde yeni bir oturum oluşturmanız gerekir.
Gerçek Çalışma Süresini Bildirme
Çalışmayı tamamlamak için gereken gerçek süreyi nanosaniye cinsinden izleyin ve her döngüde çalışma tamamlandığında bunu sisteme bildirin. Örneğin, bu iş parçacıkları oluşturma içinse her karede bunu çağırın.
Gerçek zamanı güvenilir bir şekilde almak için şunları kullanın:
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>
Java
System.nanoTime();
Örneğin:
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);
Java
long startTime = System.nanoTime();
// do work
long endTime = System.nanoTime();
long duration = endTime - startTime;
hintSession.reportActualWorkDuration(duration);
Gerektiğinde hedef çalışma süresini güncelleme
Hedef çalışma süreniz her değiştiğinde (ör. oyuncu farklı bir hedef FPS seçtiğinde) updateTargetWorkDuration
yöntemini çağırarak sisteme bilgi verin. Böylece işletim sistemi, kaynakları yeni hedefe göre ayarlayabilir. Her karede çağırmanız gerekmez. Yalnızca hedef süre değiştiğinde çağırmanız yeterlidir.
C++
APerformanceHint_updateTargetWorkDuration(hint_session, target_duration);
Java
hintSession.updateTargetWorkDuration(targetDuration);