परफ़ॉर्मेंस हिंट एपीआई

रिलीज़ की गई:

Android 12 (एपीआई लेवल 31) - परफ़ॉर्मेंस हिंट एपीआई

Android 13 (एपीआई लेवल 33) - एनडीके एपीआई में परफ़ॉर्मेंस हिंट मैनेजर

(प्रीव्यू) Android 15 (DP1) - reportActualWorkDuration()

सीपीयू की परफ़ॉर्मेंस के बारे में जानकारी देने वाली सुविधा की मदद से, गेम अपनी ज़रूरतों के हिसाब से सीपीयू की परफ़ॉर्मेंस को डाइनैमिक तरीके से मैनेज कर सकता है. ज़्यादातर डिवाइसों पर, Android पिछली मांगों के आधार पर, वर्कलोड के लिए सीपीयू की क्लॉक स्पीड और कोर टाइप को डाइनैमिक तरीके से अडजस्ट करता है. अगर किसी वर्कलोड में ज़्यादा सीपीयू संसाधनों का इस्तेमाल होता है, तो क्लॉक स्पीड बढ़ जाती है. साथ ही, वर्कलोड को बड़े कोर में ले जाया जाता है. अगर वर्कलोड में कम संसाधनों का इस्तेमाल किया जाता है, तो Android संसाधनों के आवंटन को कम कर देता है. एडीपीएफ़ की मदद से, ऐप्लिकेशन या गेम अपनी परफ़ॉर्मेंस और समयसीमा के बारे में अतिरिक्त सिग्नल भेज सकता है. इससे सिस्टम को परफ़ॉर्मेंस बेहतर करने के लिए, ज़्यादा तेज़ी से काम करने में मदद मिलती है. साथ ही, काम पूरा होने पर क्लॉक को तेज़ी से कम करने में मदद मिलती है, जिससे बिजली की खपत कम होती है.

क्लॉक स्पीड

जब Android डिवाइस, सीपीयू की क्लॉक स्पीड को डाइनैमिक तरीके से अडजस्ट करते हैं, तो फ़्रीक्वेंसी आपके कोड की परफ़ॉर्मेंस को बदल सकती है. डाइनैमिक क्लॉक स्पीड को ध्यान में रखकर कोड डिज़ाइन करना ज़रूरी है. इससे परफ़ॉर्मेंस को बेहतर बनाने, सुरक्षित थर्मल स्टेट को बनाए रखने, और पावर का सही तरीके से इस्तेमाल करने में मदद मिलती है. ऐप्लिकेशन कोड में, सीपीयू फ़्रीक्वेंसी सीधे तौर पर असाइन नहीं की जा सकतीं. इसलिए, ऐप्लिकेशन के लिए ज़्यादा सीपीयू क्लॉक स्पीड पर चलने का एक सामान्य तरीका यह है कि वे बैकग्राउंड थ्रेड में बिज़ी लूप चलाएं, ताकि वर्कलोड ज़्यादा लगे. यह एक खराब तरीका है, क्योंकि इससे बैटरी की खपत होती है. साथ ही, जब ऐप्लिकेशन अतिरिक्त संसाधनों का इस्तेमाल नहीं कर रहा होता है, तब डिवाइस पर थर्मल लोड बढ़ जाता है. सीपीयू PerformanceHint एपीआई को इस समस्या को हल करने के लिए डिज़ाइन किया गया है. सिस्टम को काम की असल अवधि और टारगेट अवधि के बारे में जानकारी देने से, Android को ऐप्लिकेशन की सीपीयू से जुड़ी ज़रूरतों के बारे में खास जानकारी मिल पाएगी. साथ ही, वह संसाधनों को बेहतर तरीके से बांट पाएगा. इससे, कम बैटरी खर्च होने पर भी डिवाइस की परफ़ॉर्मेंस बेहतर होगी.

कोर टाइप

आपके गेम को चलाने के लिए इस्तेमाल किए जाने वाले सीपीयू कोर टाइप भी परफ़ॉर्मेंस का एक अहम फ़ैक्टर है. Android डिवाइस, थ्रेड को असाइन किए गए सीपीयू कोर को अक्सर बदल देते हैं. ऐसा, हाल ही के वर्कलोड के आधार पर किया जाता है. एक से ज़्यादा तरह के कोर वाले एसओसी पर सीपीयू कोर असाइन करना और भी मुश्किल होता है. इनमें से कुछ डिवाइसों पर, बड़े कोर का इस्तेमाल कुछ समय के लिए ही किया जा सकता है. इसके बाद, डिवाइस का तापमान बहुत ज़्यादा बढ़ जाता है.

आपका गेम इन वजहों से सीपीयू कोर अफ़िनिटी सेट करने की कोशिश नहीं करनी चाहिए:

  • किसी वर्कलोड के लिए सबसे सही कोर टाइप, डिवाइस के मॉडल के हिसाब से अलग-अलग होता है.
  • ज़्यादा कोर का इस्तेमाल करने की सुविधा, SoC और हर डिवाइस मॉडल के लिए उपलब्ध अलग-अलग थर्मल समाधानों के हिसाब से अलग-अलग होती है.
  • थर्मल स्टेट पर पर्यावरण के असर की वजह से, कोर चुनने में और भी मुश्किल हो सकती है. उदाहरण के लिए, मौसम या फ़ोन के कवर की वजह से, डिवाइस की थर्मल स्थिति बदल सकती है.
  • मुख्य डिवाइसों के चुनाव में, बेहतर परफ़ॉर्मेंस और थर्मल क्षमताओं वाले नए डिवाइसों को शामिल नहीं किया जा सकता. इस वजह से, डिवाइस अक्सर गेम के प्रोसेसर अफ़िनिटी को अनदेखा कर देते हैं.

Linux के डिफ़ॉल्ट शेड्यूलर के काम करने के तरीके का उदाहरण

Linux शेड्यूलर का व्यवहार
पहली इमेज. गवर्नर को सीपीयू की फ़्रीक्वेंसी को बढ़ाने या कम करने में ~200 मि॰से॰ लग सकते हैं. ADPF, डाइनैमिक वोल्टेज और फ़्रीक्वेंसी स्केलिंग (डीवीएफ़एस) सिस्टम के साथ काम करता है, ताकि हर वॉट पर सबसे अच्छी परफ़ॉर्मेंस दी जा सके

PerformanceHint API, DVFS की लेटेंसी के अलावा और भी कई चीज़ों को ऐब्स्ट्रैक्ट करता है

ADPF, DVFS की लेटेंसी से ज़्यादा जानकारी इकट्ठा करता है
दूसरी इमेज. ADPF को पता होता है कि आपकी ओर से सबसे सही फ़ैसला कैसे लेना है
  • अगर टास्क को किसी खास सीपीयू पर चलाना है, तो PerformanceHint API को पता होता है कि आपकी ओर से यह फ़ैसला कैसे लेना है.
  • इसलिए, आपको अफ़िनिटी का इस्तेमाल करने की ज़रूरत नहीं है.
  • डिवाइसों में अलग-अलग टोपोलॉजी होती हैं. पावर और थर्मल की विशेषताएं इतनी अलग-अलग होती हैं कि उन्हें ऐप्लिकेशन डेवलपर को नहीं दिखाया जा सकता.
  • आपको उस सिस्टम के बारे में कोई अनुमान नहीं लगाना चाहिए जिस पर आपका ऐप्लिकेशन चल रहा है.

समाधान

ADPF, PerformanceHintManager क्लास उपलब्ध कराता है, ताकि गेम, Android को परफ़ॉर्मेंस के बारे में जानकारी भेज सकें. जैसे, सीपीयू की क्लॉक स्पीड और कोर टाइप. इसके बाद, ओएस यह तय कर सकता है कि डिवाइस के एसओसी और थर्मल सॉल्यूशन के आधार पर, हिंट का सबसे सही तरीके से इस्तेमाल कैसे किया जाए. अगर आपका ऐप्लिकेशन, थर्मल स्टेट मॉनिटरिंग के साथ-साथ इस एपीआई का इस्तेमाल करता है, तो यह ओएस को ज़्यादा जानकारी वाले सुझाव दे सकता है. इसके बजाय, यह व्यस्त लूप और अन्य कोडिंग तकनीकों का इस्तेमाल करता है. इनसे थ्रॉटलिंग हो सकती है.

कोई गेम, परफ़ॉर्मेंस के बारे में सुझाव देने वाली सुविधा का इस्तेमाल इस तरह करता है:

  1. एक जैसे व्यवहार वाली मुख्य थ्रेड के लिए, हिंट सेशन बनाएं. उदाहरण के लिए:
    • रेंडरिंग थ्रेड और उसकी डिपेंडेंसी को एक सेशन मिलता है
      1. Cocos में, मुख्य इंजन थ्रेड और रेंडर थ्रेड को एक सेशन मिलता है
      2. Unity में, Adaptive Performance Android Provider प्लगिन को इंटिग्रेट करें
      3. Unreal में, Unreal Adaptive Performance प्लगिन इंटिग्रेट करें और कई क्वालिटी लेवल के लिए, स्केलेबिलिटी के विकल्प इस्तेमाल करें
    • IO थ्रेड को एक और सेशन मिलता है
    • ऑडियो थ्रेड को तीसरा सेशन मिलता है
  2. गेम को यह काम, सेशन के लिए सिस्टम के ज़्यादा संसाधनों की ज़रूरत पड़ने से पहले ही कर लेना चाहिए. यह काम कम से कम 2 मि॰से॰ और बेहतर होगा कि 4 मि॰से॰ से ज़्यादा समय पहले किया जाए.
  3. हर हिंट सेशन में, यह अनुमान लगाएं कि हर सेशन को चलने में कितना समय लगेगा. आम तौर पर, यह अवधि फ़्रेम इंटरवल के बराबर होती है. हालांकि, अगर वर्कलोड में फ़्रेम के हिसाब से ज़्यादा अंतर नहीं होता है, तो ऐप्लिकेशन कम इंटरवल का इस्तेमाल कर सकता है.

इस सिद्धांत को लागू करने का तरीका यहां बताया गया है:

PerformanceHintManager को शुरू करना और createHintSession बनाना

सिस्टम सेवा का इस्तेमाल करके मैनेजर पाएं और एक ही वर्कलोड पर काम करने वाले अपने थ्रेड या थ्रेड ग्रुप के लिए, एक हिंट सेशन बनाएं.

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);

अगर ज़रूरी हो, तो थ्रेड सेट करें

रिलीज़ की गई:

Android 11 (एपीआई लेवल 34)

जब आपको बाद में अन्य थ्रेड जोड़ने हों, तब PerformanceHintManager.Session के setThreads फ़ंक्शन का इस्तेमाल करें. उदाहरण के लिए, अगर आपने फ़िज़िक्स थ्रेड बाद में बनाई है और आपको उसे सेशन में जोड़ना है, तो इस setThreads एपीआई का इस्तेमाल किया जा सकता है.

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);

अगर आपको कम एपीआई लेवल को टारगेट करना है, तो आपको सेशन को बंद करना होगा. साथ ही, थ्रेड आईडी बदलने के लिए, हर बार एक नया सेशन बनाना होगा.

काम की अवधि की रिपोर्ट करना

काम पूरा करने में लगने वाले समय को नैनोसेकंड में ट्रैक करें. साथ ही, हर साइकल में काम पूरा होने पर, इसकी जानकारी सिस्टम को दें. उदाहरण के लिए, अगर यह आपके रेंडरिंग थ्रेड के लिए है, तो इसे हर फ़्रेम पर कॉल करें.

समय की सटीक जानकारी पाने के लिए, इनका इस्तेमाल करें:

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();

उदाहरण के लिए:

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);

ज़रूरत पड़ने पर, टारगेट वर्क ड्यूरेशन अपडेट करना

जब भी टारगेट वर्क ड्यूरेशन में बदलाव होता है, तब updateTargetWorkDuration तरीके को कॉल करें. उदाहरण के लिए, अगर प्लेयर कोई दूसरा टारगेट एफ़पीएस चुनता है, तो सिस्टम को इसकी जानकारी दें, ताकि ओएस नए टारगेट के हिसाब से संसाधनों को अडजस्ट कर सके. आपको इसे हर फ़्रेम पर कॉल करने की ज़रूरत नहीं है. आपको इसे सिर्फ़ तब कॉल करना होगा, जब टारगेट अवधि बदलती है.

C++

APerformanceHint_updateTargetWorkDuration(hint_session, target_duration);

Java

hintSession.updateTargetWorkDuration(targetDuration);