পারফর্ম্যান্স হিন্ট API

রিলিজ করা হয়েছে:

Android 12 (API লেভেল 31) - PerformanceHintManager

Android 13 (API লেভেল 33) - NDK API-তে পারফর্ম্যান্স হিন্ট ম্যানেজার

Android 15 (DP1) - reportActualWorkDuration()

সিপিইউ পারফর্ম্যান্স সংক্রান্ত হিন্টের মাধ্যমে, কোনও অ্যাপ তার প্রয়োজন আরও ভালোভাবে মেটাতে ডায়নামিক সিপিইউ পারফর্ম্যান্স আচরণকে প্রভাবিত করতে পারে। বেশিরভাগ ডিভাইসে, Android, পূর্ববর্তী চাহিদা অনুযায়ী ওয়ার্কলোডের জন্য CPU ক্লক স্পিড ও কোরের ধরন ডায়নামিক অ্যাডজাস্ট করে। কোনও ওয়ার্কলোড বেশি CPU রিসোর্স ব্যবহার করলে, ক্লক স্পিড বাড়ানো হয় এবং ওয়ার্কলোডটি শেষ পর্যন্ত একটি বড় কোরে সরানো হয়। ওয়ার্কলোড কম রিসোর্স ব্যবহার করলে, Android রিসোর্স অ্যালোকেশন কমিয়ে দেয়। ADPF-এর মাধ্যমে, কোনও অ্যাপ এর পারফর্ম্যান্স ও সময়সীমা সম্পর্কে অতিরিক্ত সিগন্যাল পাঠাতে পারে। এর ফলে সিস্টেম আরও বেশি করে ক্লক স্পিড বাড়াতে (পারফর্ম্যান্স উন্নত করতে) এবং ওয়ার্কলোড সম্পূর্ণ হয়ে গেলে দ্রুত ক্লক স্পিড কমাতে (বিদ্যুৎ সাশ্রয় করতে) পারে।

ক্লক স্পিড

Android ডিভাইস যখন ডায়নামিকালি তার CPU ক্লক স্পিড অ্যাডজাস্ট করে, তখন ফ্রিকোয়েন্সি আপনার কোডের পারফর্ম্যান্স পরিবর্তন করতে পারে। পারফর্ম্যান্স সর্বাধিক করা, নিরাপদ থার্মাল স্টেট বজায় রাখা এবং বিদ্যুৎ দক্ষতার সাথে ব্যবহার করার জন্য ডাইনামিক ক্লক স্পিড অ্যাড্রেস করে এমন কোড ডিজাইন করা গুরুত্বপূর্ণ। আপনি সরাসরি আপনার অ্যাপ কোডে CPU ফ্রিকোয়েন্সি অ্যাসাইন করতে পারবেন না। এর ফলে, অ্যাপের জন্য আরও বেশি CPU ক্লক স্পিডে রান করার চেষ্টা করার একটি সাধারণ উপায় হল ব্যাকগ্রাউন্ড থ্রেডে একটি বিজি লুপ রান করা যাতে ওয়ার্কলোড আরও বেশি ডিমান্ডিং বলে মনে হয়। এটি একটি খারাপ অভ্যাস কারণ এটি বিদ্যুৎ নষ্ট করে এবং অ্যাপটি আসলে অতিরিক্ত রিসোর্স ব্যবহার না করার সময় ডিভাইসে থার্মাল লোড বাড়ায়। এই সমস্যার সমাধান করার জন্য CPU PerformanceHint API ডিজাইন করা হয়েছে। সিস্টেমকে প্রকৃত কাজের সময়সীমা ও টার্গেট কাজের সময়সীমা সম্পর্কে জানিয়ে, Android অ্যাপের সিপিইউ প্রয়োজন সম্পর্কে একটি ওভারভিউ পেতে এবং দক্ষতার সাথে রিসোর্স বরাদ্দ করতে পারবে। এর ফলে বিদ্যুৎ খরচ কম হবে এবং পারফর্ম্যান্স সবচেয়ে ভালো হবে।

কোর টাইপ

আপনার অ্যাপ যে CPU কোর টাইপে চলে সেটিও পারফর্ম্যান্সের একটি গুরুত্বপূর্ণ ফ্যাক্টর। Android ডিভাইস প্রায়ই সাম্প্রতিক ওয়ার্কলোড আচরণের উপর ভিত্তি করে ডায়নামিকালি থ্রেডে অ্যাসাইন করা CPU কোর পরিবর্তন করে। একাধিক ধরনের কোর সহ SoC-তে CPU কোর অ্যাসাইনমেন্ট আরও বেশি জটিল। এইসব ডিভাইসের মধ্যে কিছু ডিভাইসে, বড় কোরগুলি থার্মালি আনসাস্টেনেবল অবস্থায় না গিয়ে শুধুমাত্র অল্প সময়ের জন্য ব্যবহার করা যেতে পারে।

আপনার অ্যাপ নিম্নলিখিত কারণে CPU কোর অ্যাফিনিটি সেট করার চেষ্টা করবে না:

  • ওয়ার্কলোডের জন্য সবচেয়ে উপযুক্ত কোর টাইপ ডিভাইসের মডেল অনুযায়ী আলাদা আলাদা হয়।
  • বড় কোর চালানোর স্থায়িত্ব SoC এবং প্রতিটি ডিভাইস মডেলের দেওয়া বিভিন্ন থার্মাল সলিউশনের উপর নির্ভর করে।
  • তাপমাত্রার উপর পরিবেশগত প্রভাব মূল বিকল্পকে আরও জটিল করে তুলতে পারে। যেমন, আবহাওয়া বা ফোনের কেস ডিভাইসের থার্মাল স্টেট পরিবর্তন করতে পারে।
  • অতিরিক্ত পারফর্ম্যান্স ও থার্মাল ক্ষমতা সহ নতুন ডিভাইস কোর সিলেকশন মানিয়ে নিতে পারে না। এর ফলে, ডিভাইস প্রায়ই কোনও অ্যাপের প্রসেসর অ্যাফিনিটি উপেক্ষা করে।

ডিফল্ট Linux শিডিউলারের আচরণের উদাহরণ

Linux শিডিউলারের আচরণ
ছবি ১. সিপিইউ ফ্রিকোয়েন্সি বাড়াতে বা কমাতে গভর্নর ~২০০ms সময় নিতে পারে। ADPF, ডায়নামিক ভোল্টেজ এবং ফ্রিকোয়েন্সি স্কেলিং সিস্টেমের (DVFS) সাথে কাজ করে প্রতি ওয়াটে সেরা পারফর্ম্যান্স প্রদান করে

PerformanceHint API, DVFS লেটেন্সির চেয়ে বেশি অ্যাবস্ট্র্যাক্ট করে

ADPF DVFS লেটেন্সির চেয়ে বেশি অ্যাবস্ট্রাক্ট করে
ছবি ২. ADPF আপনার হয়ে কীভাবে সবচেয়ে ভাল সিদ্ধান্ত নিতে হয় তা জানে
  • কোনও নির্দিষ্ট CPU-তে টাস্ক চালানোর প্রয়োজন হলে, PerformanceHint API জানে যে কীভাবে আপনার হয়ে সেই সিদ্ধান্ত নিতে হয়।
  • তাই, আপনাকে অ্যাফিনিটি ব্যবহার করতে হবে না।
  • ডিভাইসগুলি বিভিন্ন টপোলজি সহ আসে; পাওয়ার ও থার্মাল বৈশিষ্ট্যগুলি অ্যাপ ডেভেলপারদের কাছে প্রকাশ করার জন্য খুব বেশি পরিবর্তিত হয়।
  • আপনি যে আন্ডারলায়িং সিস্টেমে রান করছেন সেটির ব্যাপারে কোনও অনুমান করতে পারবেন না।

সমাধান

ADPF PerformanceHintManager ক্লাস প্রদান করে, এর ফলে অ্যাপগুলি CPU ক্লক স্পিড ও কোর টাইপের জন্য Android-এ পারফর্ম্যান্স হিন্ট পাঠাতে পারে। তারপরে, OS, SoC এবং ডিভাইসের থার্মাল সলিউশনের উপর ভিত্তি করে কীভাবে সবচেয়ে ভালোভাবে হিন্ট ব্যবহার করা যায় সেই ব্যাপারে সিদ্ধান্ত নিতে পারে। আপনার অ্যাপ থার্মাল স্টেট মনিটরিংয়ের সাথে এই API ব্যবহার করলে, এটি OS-কে আরও তথ্যপূর্ণ ইঙ্গিত দিতে পারে। এর ফলে, বিজি লুপ এবং অন্যান্য কোডিং টেকনিক ব্যবহার করতে হয় না, যা থ্রটলিংয়ের কারণ হতে পারে।

কীভাবে তত্ত্বকে বাস্তবে প্রয়োগ করবেন তা এখানে দেওয়া হল:

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

জাভা

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 (API লেভেল 34)

পরে যোগ করতে হবে এমন অন্যান্য থ্রেড থাকলে PerformanceHintManager.Session-এর setThreads ফাংশন ব্যবহার করুন। যেমন, আপনি যদি পরে নিজের ফিজিক্স থ্রেড তৈরি করেন এবং সেশনে এটি যোগ করতে চান, তাহলে এই setThreads API ব্যবহার করতে পারবেন।

C++

auto tids = thread_ids.data();
std::size_t size = thread_ids_.size();
APerformanceHint_setThreads(hint_session, tids, size);

জাভা

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

আপনি যদি আরও কম API লেভেল টার্গেট করেন, তাহলে আপনাকে সেশনটি ধ্বংস করতে হবে এবং থ্রেড আইডি পরিবর্তন করার প্রয়োজন হলে প্রতিবার একটি নতুন সেশন আবার তৈরি করতে হবে।

বাস্তবে কাজের সময়সীমা সম্পর্কে অভিযোগ জানানো

কাজটি সম্পূর্ণ করতে কতক্ষণ সময় লেগেছে তা ন্যানোসেকেন্ডে ট্র্যাক করুন এবং প্রতিটি চক্রে কাজ সম্পূর্ণ হওয়ার পরে সিস্টেমকে রিপোর্ট করুন। যেমন, এটি যদি আপনার রেন্ডারিং থ্রেডের জন্য হয়, তাহলে প্রতিটি ফ্রেমে এটি কল করুন।

নির্ভরযোগ্যভাবে প্রকৃত সময় পেতে, এটি ব্যবহার করুন:

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>

জাভা

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

জাভা

long startTime = System.nanoTime();

// do work

long endTime = System.nanoTime();
long duration = endTime - startTime;

hintSession.reportActualWorkDuration(duration);

প্রয়োজন হলে টার্গেট কাজের সময়সীমা আপডেট করুন

আপনার টার্গেট কাজের সময়সীমা যখনই পরিবর্তিত হয়, যেমন প্লেয়ার যদি অন্য কোনও টার্গেট fps বেছে নেয়, তাহলে updateTargetWorkDuration মেথড কল করুন যাতে সিস্টেম জানতে পারে এবং OS নতুন টার্গেট অনুযায়ী রিসোর্স অ্যাডজাস্ট করতে পারে। আপনাকে প্রতিটি ফ্রেমে এটি কল করতে হবে না এবং টার্গেট করা সময়সীমা পরিবর্তন হলে শুধুমাত্র তখনই এটি কল করতে হবে।

C++

APerformanceHint_updateTargetWorkDuration(hint_session, target_duration);

জাভা

hintSession.updateTargetWorkDuration(targetDuration);