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

रिलीज़ की गई:

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

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

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

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

क्लॉक स्पीड

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

कोर टाइप

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

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

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

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

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

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

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

समाधान

ADPF, PerformanceHintManager क्लास उपलब्ध कराता है, ताकि ऐप्लिकेशन, Android को परफ़ॉर्मेंस के बारे में जानकारी भेज सकें. इससे 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)

जब आपको बाद में अन्य थ्रेड जोड़ने हों, तब 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);