Performans İpucu API'sı

Yayın tarihi:

Android 12 (API düzeyi 31) - PerformanceHintManager

Android 13 (API düzeyi 33) - NDK API'sinde Performans İpucu Yöneticisi

Android 15 (DP1) - reportActualWorkDuration()

CPU performans ipuçları sayesinde bir uygulama, ihtiyaçlarına daha iyi uyacak şekilde dinamik CPU performans davranışını 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 bir uygulama, 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 kodlar 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, uygulamanın ek kaynakları kullanmadığı durumlarda güç israfına ve cihazdaki termal yükün artmasına neden olduğundan kötü bir uygulamadır. CPU PerformanceHint API'si bu sorunu gidermek için tasarlanmıştır. Sisteme gerçek çalışma süresini ve hedef çalışma süresini bildirerek Android, uygulamanın CPU ihtiyaçlarına genel bir bakış elde edebilir ve kaynakları verimli bir şekilde tahsis edebilir. Bu, verimli güç tüketimi düzeyinde optimum performansa yol açar.

Temel türler

Uygulamanızın üzerinde çalıştığı CPU çekirdek türleri de önemli bir performans faktörüdür. Android cihazlar, bir iş parçacığına atanan CPU çekirdeğini son iş yükü davranışına göre genellikle 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.

Uygulamanız 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 durum üzerindeki çevresel etki, çekirdek seçimini daha da karmaşık hale getirebilir. Örneğin, hava durumu veya telefon kılıfı, cihazın termal durumunu değiştirebilir.
  • Temel seçim, ek performans ve termal özelliklere sahip yeni cihazları desteklemez. Bu nedenle cihazlar genellikle bir uygulamanın işlemci yakınlığını göz ardı eder.

Varsayılan Linux zamanlayıcı davranışına örnek

Linux Zamanlayıcı Davranışı
Şekil 1. Governor, CPU frekansını artırmak veya azaltmak için yaklaşık 200 ms sürebilir. ADPF, watt başına en iyi performansı sağlamak için Dinamik Voltaj ve Frekans Ölçeklendirme sistemiyle (DVFS) birlikte çalışır.

PerformanceHint API, DVFS gecikmelerinden daha fazlasını soyutlar

ADPF, DVFS gecikmelerinden daha fazla soyutlama yapar
Şekil 2. ADPF, sizin adınıza en iyi kararı nasıl vereceğini bilir
  • 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, uygulamaları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 kısıtlamaya neden olabilecek diğer kodlama tekniklerini kullanmak yerine işletim sistemine daha bilinçli ipuçları sağlayabilir.

Teoriyi pratiğe nasıl geçireceğiniz aşağıda açıklanmıştır:

PerformanceHintManager'ı başlatma ve createHintSession'ı oluşturma

Sisteme ait hizmeti 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 seviyelerini 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

İşin tamamlanması için gereken gerçek süreyi nanosaniye cinsinden takip edin ve her döngüde iş tamamlandığında 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);