Performans İpucu API'sı

Yayınlanma tarihi:

Android 12 (API Düzeyi 31) - Performance ipucu API

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

(Önizleme) Android 15 (DP1) - reportActualWorkDuration()

CPU performans ipuçları sayesinde bir oyun, dinamik CPU performansı davranışını ihtiyaçlarına daha uygun olacak şekilde etkileyebilir. Android çoğu cihazda, önceki taleplere göre iş yükleri 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ü sonuçta daha büyük bir çekirdeğe taşınır. İş yükü daha az kaynak kullanıyorsa Android kaynak ayırmayı azaltır. ADPF sayesinde uygulama veya oyun, performansı ve son tarihleri hakkında ek bir sinyal gönderebilir. Bu, sistemin daha agresif bir şekilde gelişmesine (performansın artırılmasına) ve iş yükü tamamlandığında saatlerin daha hızlı indirilmesine (güç kullanımından tasarruf edilmesine) 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ına yönelik 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. Sonuç olarak, uygulamaların daha yüksek CPU saat hızlarında çalışmayı denemelerinin yaygın bir yolu, arka plan iş parçacığında yoğun bir döngü çalıştırmaktır. Böylece iş yükü, daha zorlu bir hâle gelir. Uygulama aslında ek kaynakları kullanmadığında güç boşa harcandığı ve cihazdaki termal yükü artırdığı için bu kötü bir uygulamadır. CPU PerformanceHint API, bu sorunu gidermek için tasarlanmıştır. Android, gerçek çalışma süresini ve hedef çalışma süresini sisteme bildirerek uygulamanın CPU ihtiyaçları hakkında genel bir bakış elde edebilir ve kaynakları verimli bir şekilde dağıtabilir. Bu sayede verimli güç tüketimi düzeyinde optimum performans elde edilir.

Temel türler

Oyununuzun üzerinde çalıştığı CPU temel türleri de diğer önemli performans faktörüdür. Android cihazlar genellikle bir iş parçacığına atanan CPU çekirdeğini, son iş yükü davranışına göre dinamik olarak değiştirir. Birden çok çekirdek türüne sahip çip üzerinde sistem (SoC) değerlerinde CPU çekirdek ataması daha da karmaşıktır. Bu cihazların bazılarında daha büyük çekirdekler, termal olarak sürdürülebilir olmayan bir duruma geçmeden yalnızca kısa bir süre kullanılabilir.

Oyununuz aşağıdaki nedenlerden dolayı CPU temel yakın ilgi alanını ayarlamaya çalışmamalıdır:

  • Bir iş yükü için en uygun temel tür, cihaz modeline göre değişir.
  • Daha büyük çekirdeklerin çalıştırılmasının sürdürülebilirliği, çip üzerinde sisteme (SoC) ve her cihaz modelinin sağladığı çeşitli termal çözümlere göre değişir.
  • Termal durum üzerindeki çevresel etki, temel seçimi daha da karmaşık hale getirebilir. Örneğin, hava durumu veya telefon kılıfı bir cihazın termal durumunu değiştirebilir.
  • Çekirdek seçim, ek performansa ve termal özelliklere sahip yeni cihazları barındıramaz. Sonuç olarak, cihazlar genellikle bir oyunun işlemci yakınlığını yoksayar.

Varsayılan Linux planlayıcı davranışı örneği

Linux Planlayıcı Davranışı
Şekil 1. Yöneticinin CPU frekansını artırması veya azaltması yaklaşık 200 ms sürebilir. ADPF, vat başına en iyi performansı sağlamak için Dinamik Voltaj ve Frekans Ölçeklendirme sistemi (DVFS) ile birlikte çalışır

PerformanceHint API, DVFS gecikmelerinden daha fazlasını soyutluyor

ADPF Soyutları, DVFS Gecikmelerinden Fazla
Şekil 2. ADPF sizin adınıza en iyi kararı nasıl verebileceğini bilir
  • Görevlerin belirli bir CPU'da çalışması gerekiyorsa PerformanceHint API bu kararı sizin adınıza nasıl vereceğini bilir.
  • Dolayısıyla, yakın ilgi alanını kullanmanız gerekmez.
  • Cihazlar çeşitli topolojilere sahiptir; güç ve termal özellikler uygulama geliştiriciye maruz kalamayacak kadar çeşitlidir.
  • Üzerinde çalıştığınız temel sistem hakkında varsayımlarda 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. Böylece işletim sistemi, cihazın çip üzerinde sisteme (SoC) 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 meşgul döngüleri ve kısıtlamaya neden olabilecek diğer kodlama teknikleri yerine işletim sistemine daha bilinçli ipuçları sağlayabilir.

Bir oyun, performans ipuçlarını şu şekilde kullanır:

  1. Benzer şekilde davranan anahtar ileti dizileri için ipucu oturumları oluşturun. Örneğin:
  2. Oyun, bir oturumun daha fazla sistem kaynağına ihtiyaç duymadan önce bu işlemi en az 2 ms., tercihen 4 ms'den uzun bir süre içinde tamamlamalıdır.
  3. Her ipucu oturumunda, her oturumun devam etmesi için gereken süreyi tahmin edin. Normal süre, kare aralığına eşdeğerdir ancak iş yükü kareler arasında önemli ölçüde farklılık göstermiyorsa uygulama daha kısa bir aralık kullanabilir.

Teoriyi pratiğe nasıl aktarabileceğinizi buradan öğrenebilirsiniz:

PerformanceHintManager ve createHintSession'ı başlatma

Sistem hizmetini kullanarak yöneticiyi 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ınlanma tarihi:

Android 11 (API Düzeyi 34)

Daha sonra eklemeniz gereken başka iş parçacıklarınız olduğunda PerformanceHintManager.Session öğesinin setThreads işlevini kullanın. Örneğin, fizik konulu ileti dizisini daha sonra oluşturursanız ve 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 iş parçacığı kimliklerini her değiştirmeniz gerektiğinde oturumu kaldırmanız ve yeni bir oturum oluşturmanız gerekir.

Rapor Gerçek Çalışma Süresi

İşin tamamlanması için gereken gerçek süreyi nanosaniye cinsinden takip edin ve her döngüde iş tamamlandıktan sonra sisteme bildirin. Örneğin, bu oluşturma iş parçacıklarınız içinse her karede bunu çağırın.

Gerçek süreyi güvenilir bir şekilde almak için aşağıdakileri 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();

Örnek:

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üncelleyin

Hedef çalışma süreniz değiştiğinde (örneğin, oyuncu farklı bir hedef fps seçerse) işletim sisteminin kaynakları yeni hedefe göre ayarlayabilmesi için sisteme bilgi vermek üzere updateTargetWorkDuration yöntemini çağırın. Her karede çağırmanız gerekmez, yalnızca hedef süre değiştiğinde çağırmanız gerekir.

C++

APerformanceHint_updateTargetWorkDuration(hint_session, target_duration);

Java

hintSession.updateTargetWorkDuration(targetDuration);