تاريخ الإصدار:
Android 12 (المستوى 31 لواجهة برمجة التطبيقات) - Performance Hint API
Android 13 (المستوى 33 لواجهة برمجة التطبيقات) - Performance Hint Manager في واجهة برمجة التطبيقات NDK
(إصدار تجريبي) Android 15 (DP1) - reportActualWorkDuration()
باستخدام تلميحات أداء وحدة المعالجة المركزية، يمكن للعبة التأثير في سلوك الأداء الديناميكي لوحدة المعالجة المركزية لتحسين مطابقة احتياجاتها. في معظم الأجهزة، يضبط نظام التشغيل Android بشكل ديناميكي سرعة الساعة لوحدة المعالجة المركزية ونوع النواة لأي عبء عمل استنادًا إلى المتطلبات السابقة. إذا كانت إحدى مهام المعالجة تستخدم المزيد من موارد وحدة المعالجة المركزية، تتم زيادة سرعة المعالجة ويتم نقل مهمة المعالجة في النهاية إلى نواة أكبر. إذا كان عبء العمل يستخدم موارد أقل، سيقلّل نظام التشغيل Android من تخصيص الموارد. باستخدام ADPF، يمكن للتطبيق أو اللعبة إرسال إشارة إضافية بشأن الأداء والمواعيد النهائية. يساعد ذلك النظام في زيادة السرعة بشكل أكثر فعالية (ما يؤدي إلى تحسين الأداء) وخفض السرعات بسرعة عند اكتمال عبء العمل (ما يؤدي إلى توفير استهلاك الطاقة).
سرعة الساعة
عندما تعدّل أجهزة Android سرعة وحدة المعالجة المركزية (CPU) ديناميكيًا، يمكن أن يؤدي التردد إلى تغيير أداء الرمز البرمجي. يُعد تصميم الرمز البرمجي الذي يتناول سرعات الساعة الديناميكية أمرًا مهمًا لتحقيق أقصى أداء والحفاظ على حالة حرارية آمنة واستخدام الطاقة بكفاءة. لا يمكنك تعيين ترددات وحدة المعالجة المركزية مباشرةً في رمز تطبيقك. ونتيجةً لذلك، تتمثّل إحدى الطرق الشائعة التي تحاول التطبيقات من خلالها التشغيل بسرعات أعلى لوحدة المعالجة المركزية في تشغيل حلقة مشغولة في سلسلة محادثات في الخلفية، ما يجعل عبء العمل يبدو أكثر تطلبًا. وهذه ممارسة سيئة لأنّها تؤدي إلى إهدار الطاقة وزيادة الحمل الحراري على الجهاز عندما لا يستخدم التطبيق الموارد الإضافية فعليًا. تم تصميم واجهة برمجة التطبيقات PerformanceHint
لوحدة المعالجة المركزية (CPU) لحلّ هذه المشكلة. من خلال إعلام النظام بمدة العمل الفعلية ومدة العمل المستهدَفة، سيتمكّن نظام التشغيل Android من الحصول على نظرة عامة على احتياجات التطبيق من وحدة المعالجة المركزية وتخصيص الموارد بكفاءة. سيؤدي ذلك إلى تحقيق الأداء الأمثل مع استهلاك الطاقة بكفاءة.
الأنواع الأساسية
تُعد أنواع أنوية وحدة المعالجة المركزية التي تعمل عليها لعبتك من العوامل المهمة الأخرى التي تؤثر في الأداء. تغيّر أجهزة Android غالبًا نواة وحدة المعالجة المركزية (CPU) المخصّصة لأحد مؤشرات الترابط بشكل ديناميكي استنادًا إلى سلوك عبء العمل الأخير. تصبح عملية تحديد عدد أنوية وحدة المعالجة المركزية أكثر تعقيدًا في أنظمة SoC التي تتضمّن أنواعًا متعددة من النوى. في بعض هذه الأجهزة، لا يمكن استخدام النوى الأكبر حجمًا إلا لفترة وجيزة بدون أن يصبح الجهاز غير مستدام حراريًا.
يجب ألا تحاول لعبتك ضبط تقارب نواة وحدة المعالجة المركزية للأسباب التالية:
- يختلف أفضل نوع من النوى لوحدة العمل حسب طراز الجهاز.
- تختلف إمكانية تشغيل النوى الأكبر حجمًا حسب المنظومة على الرقاقة (SoC) وحسب الحلول الحرارية المختلفة التي يوفّرها كل طراز جهاز.
- يمكن أن يؤدي التأثير البيئي على الحالة الحرارية إلى تعقيد عملية اختيار النواة بشكل أكبر. على سبيل المثال، يمكن أن يؤدي الطقس أو حافظة الهاتف إلى تغيير الحالة الحرارية للجهاز.
- لا يمكن أن يستوعب اختيار النواة الأجهزة الجديدة التي تتضمّن أداءً وقدرات حرارية إضافية. ونتيجةً لذلك، تتجاهل الأجهزة غالبًا توافق معالج اللعبة.
مثال على السلوك التلقائي لمجدول Linux

تجرِّد واجهة برمجة التطبيقات PerformanceHint أكثر من أوقات استجابة DVFS

- إذا كانت المهام بحاجة إلى التنفيذ على وحدة معالجة مركزية (CPU) معيّنة، تعرف واجهة برمجة التطبيقات PerformanceHint كيفية اتّخاذ هذا القرار نيابةً عنك.
- لذلك، لا تحتاج إلى استخدام سمة الاهتمام المشترك.
- تتضمّن الأجهزة أشكالاً مختلفة، كما أنّ خصائص الطاقة والحرارة تختلف بشكل كبير ولا يمكن عرضها لمطوّر التطبيق.
- لا يمكنك وضع أي افتراضات بشأن النظام الأساسي الذي تعمل عليه.
الحل
توفّر واجهة برمجة التطبيقات ADPF الفئة PerformanceHintManager
، ما يتيح للألعاب إرسال تلميحات الأداء إلى Android بشأن سرعة وحدة المعالجة المركزية ونوع النواة. يمكن لنظام التشغيل بعد ذلك تحديد أفضل طريقة لاستخدام التلميحات استنادًا إلى نظام المعالجة على الشريحة (SoC) وحل التبريد في الجهاز. إذا كان تطبيقك يستخدم واجهة برمجة التطبيقات هذه إلى جانب مراقبة الحالة الحرارية، يمكنه تقديم تلميحات أكثر استنارة إلى نظام التشغيل بدلاً من استخدام الحلقات المشغولة وتقنيات الترميز الأخرى التي يمكن أن تؤدي إلى الحدّ من سرعة المعالجة.
في ما يلي كيفية استخدام إشارات الأداء في إحدى الألعاب:
- إنشاء جلسات تلميحات لسلاسل المحادثات الرئيسية التي تتشابه في سلوكها على سبيل المثال:
- يحصل مؤشر ترابط العرض وتبعياته على جلسة واحدة
- في Cocos، تحصل سلسلة التعليمات الرئيسية للمحرّك وسلسلة تعليمات العرض على جلسة واحدة.
- في Unity، يمكنك دمج مكوّن Adaptive Performance Android Provider الإضافي
- في Unreal، يمكنك دمج مكوّن Unreal Adaptive Performance الإضافي واستخدام خيارات قابلية التوسّع لتوفير مستويات جودة متعددة.
- تحصل سلاسل IO على جلسة أخرى
- تحصل سلاسل المحادثات الصوتية على جلسة ثالثة
- يحصل مؤشر ترابط العرض وتبعياته على جلسة واحدة
- يجب أن تنفّذ اللعبة هذا الإجراء مبكرًا، قبل 2 ملي ثانية على الأقل، ويُفضّل أن يكون ذلك قبل أكثر من 4 ملي ثانية من الوقت الذي تحتاج فيه الجلسة إلى زيادة موارد النظام.
- في كل جلسة تلميحات، توقَّع المدة اللازمة لتنفيذ كل جلسة. تكون المدة النموذجية مساوية لفاصل زمني بين اللقطات، ولكن يمكن للتطبيق استخدام فاصل زمني أقصر إذا لم يختلف عبء العمل بشكل كبير بين اللقطات.
في ما يلي كيفية تطبيق هذه النظرية:
تهيئة PerformanceHintManager وإنشاء hintSession
احصل على المدير باستخدام خدمة النظام وأنشئ جلسة تلميح للمؤشر أو مجموعة المؤشرات التي تعمل على عبء العمل نفسه.
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);
إذا كنت تستهدف مستويات أدنى من واجهة برمجة التطبيقات، عليك إيقاف الجلسة وإعادة إنشائها في كل مرة تحتاج فيها إلى تغيير أرقام تعريف سلاسل المحادثات.
Report Actual Work Duration
تتبُّع المدة الفعلية اللازمة لإكمال العمل بالنانو ثانية وإبلاغ النظام بها عند إكمال العمل في كل دورة على سبيل المثال، إذا كان هذا الإجراء مخصّصًا لسلاسل عرض المحتوى، عليك استدعاؤه في كل إطار.
للحصول على الوقت الفعلي بشكل موثوق، استخدِم ما يلي:
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);