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ışı](https://developer.android.com/static/games/optimize/adpf/images/adpf_linux_sched_behavior.png?authuser=5&hl=tr)
PerformanceHint API, DVFS gecikmelerinden daha fazlasını soyutluyor
![ADPF Soyutları, DVFS Gecikmelerinden Fazla](https://developer.android.com/static/games/optimize/adpf/images/adpf_dvfs_latency.png?authuser=5&hl=tr)
- 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:
- Benzer şekilde davranan anahtar ileti dizileri için ipucu oturumları oluşturun. Örneğin:
- Oluşturma iş parçacığı ve bağımlılıkları tek bir oturum alıyor
- Cocos'ta ana motor iş parçacığı ve oluşturma iş parçacığı bir oturum alır
- Unity'de Uyarlanabilir Performans Android Sağlayıcı eklentisini entegre edin
- Unreal'de, Unreal Uyarlanabilir Performans eklentisini entegre edin ve birden fazla kalite seviyesini desteklemek için Ölçeklenebilirlik seçeneklerini kullanın
- IO ileti dizileri başka bir oturum alıyor
- Ses ileti dizileri üçüncü oturumda
- Oluşturma iş parçacığı ve bağımlılıkları tek bir oturum alıyor
- 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.
- 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);