Performans İpucu API'sı

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 uygulama, 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 bir uygulama, performansı ve son tarihler hakkında ek bir sinyal gönderebilir. Bu sayede sistem, daha agresif bir şekilde hızlanarak performansı artırır ve iş yükü tamamlandığında saatleri hızlıca düşürerek güç kullanımını azaltır.

Saat hızı

Android cihazlar CPU saat hızını dinamik olarak ayarladığında frekans, kodunuzun performansını değiştirebilir. Performansı en üst düzeye çıkarmak, güvenli bir termal durumu korumak ve gücü verimli kullanmak için dinamik saat hızlarını ele alan kodlar tasarlamak ö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 sağlar.

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ş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.

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 zorlaştırabilir. Ö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 uygulamanın işlemci yakınlığını göz ardı eder.

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

Linux Zamanlayıcı Davranışı
1. şekil. 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 birlikte 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 geçirmek için yapmanız gerekenler:

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

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 takip edin ve her döngüde çalışma 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 sistemi bilgilendirin. 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);