মুক্তি :
Android 12 (API লেভেল 31) - পারফরম্যান্স হিন্ট API
Android 13 (API লেভেল 33) - NDK API-এ পারফরম্যান্স হিন্ট ম্যানেজার
(প্রিভিউ) Android 15 (DP1) - reportActualWorkDuration()
CPU পারফরম্যান্স ইঙ্গিত সহ, একটি গেম তার চাহিদার সাথে আরও ভালভাবে মেলে গতিশীল CPU কর্মক্ষমতা আচরণকে প্রভাবিত করতে পারে। বেশিরভাগ ডিভাইসে, পূর্ববর্তী চাহিদার উপর ভিত্তি করে একটি কাজের চাপের জন্য Android গতিশীলভাবে CPU ঘড়ির গতি এবং মূল ধরন সামঞ্জস্য করে। যদি কোনও কাজের চাপ বেশি CPU সংস্থান ব্যবহার করে, ঘড়ির গতি বাড়ানো হয় এবং কাজের চাপ শেষ পর্যন্ত একটি বড় কোরে স্থানান্তরিত হয়। যদি কাজের চাপ কম সম্পদ ব্যবহার করে, তাহলে অ্যান্ড্রয়েড সম্পদ বরাদ্দ কমিয়ে দেয়। ADPF এর সাথে, অ্যাপ্লিকেশন বা গেমটি তার কার্যকারিতা এবং সময়সীমা সম্পর্কে একটি অতিরিক্ত সংকেত পাঠাতে পারে। এটি সিস্টেমকে আরও আক্রমনাত্মকভাবে র্যাম্প করতে সাহায্য করে (কর্মক্ষমতা উন্নত করে) এবং কাজের চাপ সম্পূর্ণ হলে দ্রুত ঘড়ি কমিয়ে দেয় (বিদ্যুতের ব্যবহার সাশ্রয় করে)।
ঘড়ির গতি
যখন অ্যান্ড্রয়েড ডিভাইসগুলি গতিশীলভাবে তাদের CPU ঘড়ির গতি সামঞ্জস্য করে, তখন ফ্রিকোয়েন্সি আপনার কোডের কার্যকারিতা পরিবর্তন করতে পারে। গতিশীল ঘড়ির গতি সম্বোধন করে এমন কোড ডিজাইন করা কর্মক্ষমতা সর্বাধিক করার জন্য, একটি নিরাপদ তাপীয় অবস্থা বজায় রাখতে এবং দক্ষতার সাথে শক্তি ব্যবহার করার জন্য গুরুত্বপূর্ণ। আপনি আপনার অ্যাপ কোডে সরাসরি CPU ফ্রিকোয়েন্সি বরাদ্দ করতে পারবেন না। ফলস্বরূপ, অ্যাপগুলিকে উচ্চতর CPU ঘড়ির গতিতে চালানোর চেষ্টা করার একটি সাধারণ উপায় হল একটি ব্যাকগ্রাউন্ড থ্রেডে একটি ব্যস্ত লুপ চালানো যাতে কাজের চাপ আরও বেশি চাহিদাপূর্ণ বলে মনে হয়। এটি একটি খারাপ অভ্যাস কারণ এটি শক্তি অপচয় করে এবং অ্যাপটি আসলে অতিরিক্ত সংস্থান ব্যবহার না করলে ডিভাইসে তাপীয় লোড বাড়ায়। CPU PerformanceHint
API এই সমস্যার সমাধান করার জন্য ডিজাইন করা হয়েছে। সিস্টেমকে প্রকৃত কাজের সময়কাল এবং টার্গেট কাজের সময়কাল জানানোর মাধ্যমে, অ্যান্ড্রয়েড অ্যাপের সিপিইউ প্রয়োজনীয়তার একটি ওভারভিউ পেতে এবং দক্ষতার সাথে সম্পদ বরাদ্দ করতে সক্ষম হবে। এটি দক্ষ শক্তি খরচ স্তরে সর্বোত্তম কর্মক্ষমতার দিকে পরিচালিত করবে।
মূল প্রকার
আপনার গেম যে CPU কোর টাইপগুলিতে চলে তা হল আরেকটি গুরুত্বপূর্ণ পারফরম্যান্স ফ্যাক্টর। অ্যান্ড্রয়েড ডিভাইসগুলি প্রায়ই সাম্প্রতিক কাজের চাপের আচরণের উপর ভিত্তি করে গতিশীলভাবে একটি থ্রেডে নির্ধারিত CPU কোর পরিবর্তন করে। সিপিইউ কোর অ্যাসাইনমেন্ট একাধিক কোর ধরনের সহ SoC তে আরও জটিল। এই ডিভাইসগুলির মধ্যে কিছুতে, বড় কোরগুলি তাপগতভাবে টেকসই অবস্থায় না গিয়ে শুধুমাত্র সংক্ষিপ্তভাবে ব্যবহার করা যেতে পারে।
নিম্নলিখিত কারণগুলির জন্য আপনার গেমের CPU কোর অ্যাফিনিটি সেট করার চেষ্টা করা উচিত নয়:
- কাজের চাপের জন্য সর্বোত্তম মূল ধরন ডিভাইসের মডেল অনুসারে পরিবর্তিত হয়।
- বড় কোর চালানোর স্থায়িত্ব SoC এবং প্রতিটি ডিভাইস মডেল দ্বারা প্রদত্ত বিভিন্ন তাপীয় সমাধান দ্বারা পরিবর্তিত হয়।
- তাপীয় অবস্থার উপর পরিবেশগত প্রভাব মূল পছন্দকে আরও জটিল করে তুলতে পারে। উদাহরণস্বরূপ, আবহাওয়া বা একটি ফোন কেস একটি ডিভাইসের তাপীয় অবস্থা পরিবর্তন করতে পারে।
- মূল নির্বাচন অতিরিক্ত কর্মক্ষমতা এবং তাপ ক্ষমতা সহ নতুন ডিভাইস মিটমাট করতে পারে না। ফলস্বরূপ, ডিভাইসগুলি প্রায়শই একটি গেমের প্রসেসরের সম্বন্ধকে উপেক্ষা করে।
ডিফল্ট লিনাক্স শিডিউলার আচরণের উদাহরণ
পারফরম্যান্সহিন্ট এপিআই ডিভিএফএস বিলম্বের চেয়ে বেশি বিমূর্ত করে
- যদি কাজগুলি একটি নির্দিষ্ট CPU-তে চালানোর প্রয়োজন হয়, তাহলে পারফরম্যান্সহিন্ট API আপনার পক্ষ থেকে কীভাবে সেই সিদ্ধান্ত নিতে হয় তা জানে৷
- অতএব, আপনি সখ্যতা ব্যবহার করতে হবে না.
- ডিভাইস বিভিন্ন টপোলজির সাথে আসে; পাওয়ার এবং তাপীয় বৈশিষ্ট্যগুলি অ্যাপ বিকাশকারীর কাছে প্রকাশ করার জন্য খুব বৈচিত্র্যময়।
- আপনি যে অন্তর্নিহিত সিস্টেমটি চালাচ্ছেন সে সম্পর্কে আপনি কোনো অনুমান করতে পারবেন না।
সমাধান
ADPF PerformanceHintManager
ক্লাস সরবরাহ করে যাতে গেমগুলি CPU ঘড়ির গতি এবং মূল প্রকারের জন্য Android-এ পারফরম্যান্স ইঙ্গিত পাঠাতে পারে। তারপরে OS সিদ্ধান্ত নিতে পারে কীভাবে ডিভাইসের SoC এবং তাপীয় সমাধানের উপর ভিত্তি করে ইঙ্গিতগুলি সর্বোত্তমভাবে ব্যবহার করা যায়। যদি আপনার অ্যাপটি থার্মাল স্টেট মনিটরিংয়ের সাথে এই APIটি ব্যবহার করে, তবে এটি ব্যস্ত লুপ এবং অন্যান্য কোডিং কৌশল ব্যবহার করার পরিবর্তে OS-কে আরও জ্ঞাত ইঙ্গিত দিতে পারে যা থ্রটলিং হতে পারে।
এইভাবে একটি গেম পারফরম্যান্স ইঙ্গিত ব্যবহার করে:
- একইভাবে আচরণ করে এমন মূল থ্রেডগুলির জন্য ইঙ্গিত সেশন তৈরি করুন । যেমন:
- রেন্ডারিং থ্রেড এবং এর নির্ভরতা একটি সেশন পায়
- কোকোসে, প্রধান ইঞ্জিন থ্রেড এবং রেন্ডার থ্রেড একটি সেশন পায়
- ইউনিটিতে, অ্যাডাপ্টিভ পারফরম্যান্স অ্যান্ড্রয়েড প্রোভাইডার প্লাগইন সংহত করুন
- অবাস্তব-এ, অবাস্তব অভিযোজিত পারফরম্যান্স প্লাগইনকে একীভূত করুন এবং একাধিক গুণমানের স্তর সমর্থন করতে স্কেলেবিলিটি বিকল্পগুলি ব্যবহার করুন
- IO থ্রেড অন্য সেশন পেতে
- অডিও থ্রেড একটি তৃতীয় অধিবেশন পেতে
- রেন্ডারিং থ্রেড এবং এর নির্ভরতা একটি সেশন পায়
- একটি সেশনের সিস্টেম রিসোর্স বাড়ানোর প্রয়োজনের আগে গেমটির এটি তাড়াতাড়ি করা উচিত, কমপক্ষে 2ms এবং পছন্দসই 4ms এর বেশি।
- প্রতিটি ইঙ্গিত সেশনে, প্রতিটি সেশন চালানোর জন্য প্রয়োজনীয় সময়কালের পূর্বাভাস দিন। সাধারণ সময়কাল একটি ফ্রেম ব্যবধানের সমতুল্য, তবে অ্যাপটি একটি ছোট ব্যবধান ব্যবহার করতে পারে যদি ফ্রেম জুড়ে কাজের চাপ উল্লেখযোগ্যভাবে পরিবর্তিত না হয়।
এই তত্ত্বটি কীভাবে অনুশীলন করা যায় তা এখানে রয়েছে:
পারফরম্যান্স হিন্ট ম্যানেজার শুরু করুন এবং হিন্টসেশন তৈরি করুন
সিস্টেম পরিষেবা ব্যবহার করে ম্যানেজার পান এবং আপনার থ্রেড বা থ্রেড গ্রুপের জন্য একই কাজের চাপে কাজ করার জন্য একটি ইঙ্গিত সেশন তৈরি করুন।
সি++
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 ব্যবহার করতে পারেন।
সি++
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 স্তরগুলিকে লক্ষ্য করে থাকেন, আপনাকে সেশনটি ধ্বংস করতে হবে এবং প্রতিবার থ্রেড আইডি পরিবর্তন করার জন্য আপনাকে একটি নতুন সেশন পুনরায় তৈরি করতে হবে।
প্রকৃত কাজের সময়কাল রিপোর্ট করুন
ন্যানোসেকেন্ডে কাজটি সম্পূর্ণ করার জন্য প্রয়োজনীয় প্রকৃত সময়কাল ট্র্যাক করুন এবং প্রতিটি চক্রে কাজ শেষ হওয়ার পরে সিস্টেমে রিপোর্ট করুন। উদাহরণস্বরূপ, যদি এটি আপনার রেন্ডারিং থ্রেডের জন্য হয় তবে প্রতিটি ফ্রেমে এটি কল করুন।
প্রকৃত সময় নির্ভরযোগ্যভাবে পেতে, ব্যবহার করুন:
সি++
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();
যেমন:
সি++
// 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 নতুন টার্গেট অনুযায়ী সংস্থানগুলি সামঞ্জস্য করতে পারে৷ আপনাকে প্রতিটি ফ্রেমে এটিকে কল করতে হবে না এবং লক্ষ্যের সময়কাল পরিবর্তন হলেই এটি কল করতে হবে।
সি++
APerformanceHint_updateTargetWorkDuration(hint_session, target_duration);
জাভা
hintSession.updateTargetWorkDuration(targetDuration);