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

रि‍लीज़ का तारीख:

Android 12 (एपीआई लेवल 31) - PerformanceHintManager

Android 13 (एपीआई लेवल 33) - एनडीके एपीआई में Performance Hint Manager

Android 15 (डीपी1) - reportActualWorkDuration()

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

क्लॉक स्पीड

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

कोर टाइप

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

आपका ऐप्लिकेशन, सीपीयू कोर ऐफ़िनिटी सेट करने की कोशिश क्यों नहीं करनी चाहिए, इसकी वजहें यहां दी गई हैं:

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

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

Linux शेड्यूलर का व्यवहार
पहला डायग्राम. गवर्नर को सीपीयू की फ़्रीक्वेंसी बढ़ाने या घटाने में करीब 200 मि॰से॰ लग सकते हैं. ADPF, Dynamic Voltage and Frequency Scaling (DVFS) सिस्टम के साथ काम करता है, ताकि हर वॉट पर सबसे अच्छी परफ़ॉर्मेंस दी जा सके

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

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

समाधान

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

थ्योरी को प्रैक्टिस में लाने का तरीका यहां बताया गया है:

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)

setThreads फ़ंक्शन का इस्तेमाल तब करें, जब आपके पास ऐसे अन्य थ्रेड हों जिन्हें बाद में जोड़ा जाना हो.PerformanceHintManager.Session उदाहरण के लिए, अगर आपने अपना फ़िज़िक्स थ्रेड बाद में बनाया है और आपको उसे सेशन में जोड़ना है, तो 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);