تاريخ الإصدار:
Android 12 (المستوى 31) - Performance Hint API
Android 13 (المستوى 33 من واجهة برمجة التطبيقات) - قسم "أدوات تحسين الأداء" في واجهة NDK API
(معاينة) Android 15 (DP1) - reportActualWorkDuration()
من خلال نصائح حول أداء وحدة المعالجة المركزية (CPU)، يمكن للألعاب التأثير في الأداء الديناميكي لوحدة المعالجة المركزية (CPU) من أجل تلبية احتياجاتها بشكل أفضل. في معظم الأجهزة، يعدّل Android ديناميكيًا سرعة ساعة وحدة المعالجة المركزية (CPU) والنوع الأساسي لحجم العمل بناءً على المتطلبات السابقة. إذا كان عبء العمل يستخدم المزيد من موارد وحدة المعالجة المركزية (CPU)، تزداد سرعة الساعة ويتم نقل عبء العمل في النهاية إلى نواة أكبر. إذا كان عبء العمل يستخدم موارد أقل، سيقلّل Android تخصيص الموارد. باستخدام ADPF، يمكن للتطبيق أو اللعبة إرسال إشارة إضافية حول أدائها ومواعيدها النهائية. يساعد ذلك النظام على زيادة النشاط (تحسين الأداء) وخفض الساعات بسرعة عند اكتمال أعباء العمل (ما يؤدي إلى توفير استهلاك الطاقة).
سرعة الساعة
عندما تضبط أجهزة Android سرعة ساعة وحدة المعالجة المركزية (CPU) ديناميكيًا، يمكن أن يغيّر معدّل التردد أداء الرمز البرمجي. من المهم تصميم التعليمات البرمجية التي تعالج سرعات الساعة الديناميكية لتحقيق أفضل أداء، والحفاظ على حالة حرارية آمنة، واستخدام الطاقة بكفاءة. لا يمكنك تعيين ترددات وحدة المعالجة المركزية
بشكل مباشر في رمز التطبيق. ونتيجة لذلك، فإن إحدى الطرق الشائعة التي تحاول بها التطبيقات العمل بسرعات أعلى على مدار وحدة المعالجة المركزية (CPU) هي تشغيل حلقة مزدحمة في سلسلة محادثات في الخلفية، وبالتالي يبدو أن أعباء العمل أكثر تعقيدًا. هذه ممارسة سيئة لأنها تهدر الطاقة وتزيد
الحمل الحراري على الجهاز عندما لا يستخدم التطبيق الموارد
الإضافية. تم تصميم واجهة برمجة تطبيقات CPU PerformanceHint
لحل هذه المشكلة. من خلال السماح للنظام بمعرفة مدة العمل الفعلية ومدة العمل المستهدفة، سيتمكن Android من الحصول على نظرة عامة حول احتياجات وحدة المعالجة المركزية (CPU) للتطبيق وتخصيص الموارد بكفاءة. سيؤدي ذلك إلى تحقيق أفضل أداء على مستوى استهلاك الطاقة.
الأنواع الأساسية
تُعدّ أنواع وحدة المعالجة المركزية (CPU) الأساسية التي تعمل عليها لعبتك عاملاً مهمًا آخر للأداء. غالبًا ما تغيّر أجهزة Android وحدات المعالجة المركزية (CPU) المخصّصة لسلسلة محادثات بشكل ديناميكي بناءً على سلوك أعباء العمل الأخير. يكون التعيين الأساسي لوحدة المعالجة المركزية أكثر تعقيدًا في المنظومة على الرقاقة (SoC) التي تتضمّن أنواعًا أساسية متعددة. في بعض هذه الأجهزة، يمكن استخدام وحدات النواة الأكبر حجمًا لفترة وجيزة فقط بدون الانتقال إلى حالة غير مستدامة حراريًا.
يجب ألا تحاول لعبتك ضبط التقارب الأساسي لوحدة المعالجة المركزية (CPU) للأسباب التالية:
- يختلف أفضل نوع أساسي لتحميل العمل باختلاف طراز الجهاز.
- تختلف استدامة تشغيل النوى الأكبر حجمًا حسب المنظومة على الرقاقة (SoC) والحلول الحرارية المختلفة التي يوفّرها كل طراز من الأجهزة.
- يمكن أن يؤدي التأثير البيئي على الحالة الحرارية إلى تعقيد الخيار الأساسي. على سبيل المثال، يمكن أن يؤدي الطقس أو حافظة الهاتف إلى تغيير الحالة الحرارية للجهاز.
- لا يمكن أن يتسع الاختيار الأساسي لأجهزة جديدة ذات أداء إضافي وإمكانات حرارية. ونتيجة لذلك، غالبًا ما تتجاهل الأجهزة الاهتمام بمعالج اللعبة.
مثال على سلوك نظام جدولة مهام Linux التلقائية
![سلوك أداة جدولة نظام التشغيل Linux](https://developer.android.com/static/games/optimize/adpf/images/adpf_linux_sched_behavior.png?authuser=5&hl=ar)
لا تحتاج واجهة برمجة التطبيقات PerformanceHint إلى أكثر من وقت استجابة DVFS
![يختزل بروتوكول ADPF وقت استجابة أكثر من وقت استجابة DVFS.](https://developer.android.com/static/games/optimize/adpf/images/adpf_dvfs_latency.png?authuser=5&hl=ar)
- فإذا كانت المهام تتطلّب تشغيل وحدة معالجة مركزية (CPU) محددة، تعرِف واجهة برمجة تطبيقات PerformanceHint كيفية اتخاذ القرار نيابةً عنك.
- لذلك، لا تحتاج إلى استخدام التقارب.
- تأتي الأجهزة مع طوبولوجيات مختلفة؛ تتنوع خصائص الطاقة والحرارة إلى حد كبير بحيث لا يمكن لمطور التطبيق التعرف عليها.
- لا يمكنك وضع أي افتراضات حول النظام الأساسي الذي تعمل عليه.
الحل
توفّر أداة ADPF الفئة PerformanceHintManager
، ما يتيح للألعاب إرسال تلميحات عن الأداء إلى Android لمعرفة سرعة ساعة وحدة المعالجة المركزية (CPU) ونوعها. يمكن لنظام التشغيل بعد ذلك تحديد أفضل طريقة لاستخدام التلميحات بناءً على المنظومة على الرقاقة (SoC) والحل الحراري للجهاز. وإذا كان تطبيقك يستخدم واجهة برمجة التطبيقات هذه مع ميزة "مراقبة الحالة الحرارية"،
يمكن أن يقدّم تطبيقك تلميحات أكثر استنارة إلى نظام التشغيل بدلاً من استخدام التكرارات الحلقية المشغولة وتقنيات الترميز الأخرى التي يمكن أن تؤدي إلى التقييد.
في ما يلي كيفية استخدام اللعبة لتلميحات الأداء:
- إنشاء جلسات تلميحات لسلاسل المحادثات الرئيسية التي تعمل بالطريقة نفسها على سبيل المثال:
- يحصل عرض سلسلة التعليمات وتبعياتها على جلسة واحدة.
- في Cocos، تحصل سلسلة المحرك الرئيسية وسلسلة العرض على جلسة واحدة.
- في Unity، يمكنك دمج المكوّن الإضافي لمقدِّمي تطبيقات Android للأداء التكيّفي.
- في 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);
إذا كنت تستهدف مستويات أدنى لواجهة برمجة التطبيقات، عليك حذف الجلسة وإعادة إنشاء جلسة جديدة في كل مرة تحتاج فيها إلى تغيير أرقام تعريف سلاسل المحادثات.
الإبلاغ عن مدة العمل الفعلية
تتبع المدة الفعلية اللازمة لإنجاز العمل نانو ثانية وإبلاغ النظام بها عند الانتهاء من العمل في كل دورة. على سبيل المثال، إذا كان هذا لسلاسل العرض لديك، فقم باستدعاءها في كل إطار.
للحصول على الوقت الفعلي بشكل موثوق، استخدِم:
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);