واجهة برمجة تطبيقات Performance Hint

تاريخ الإصدار:

‫Android 12 (المستوى 31 لواجهة برمجة التطبيقات) - PerformanceHintManager

‫Android 13 (المستوى 33 لواجهة برمجة التطبيقات) - Performance Hint Manager في واجهة برمجة التطبيقات NDK

‫Android 15 (الإصدار التجريبي الأول) - reportActualWorkDuration()

من خلال تلميحات أداء وحدة المعالجة المركزية، يمكن للتطبيق التأثير في سلوك الأداء الديناميكي لوحدة المعالجة المركزية بما يتناسب مع احتياجاته بشكل أفضل. في معظم الأجهزة، يضبط Android بشكل ديناميكي سرعة ساعة وحدة المعالجة المركزية ونوع النواة لمعالجة عبء العمل استنادًا إلى الطلبات السابقة. إذا كان عبء العمل يستخدم المزيد من موارد وحدة المعالجة المركزية، يتم زيادة سرعة الساعة ويتم في النهاية نقل عبء العمل إلى نواة أكبر. إذا كان عبء العمل يستخدم موارد أقل، يخفّض Android تخصيص الموارد. باستخدام ADPF، يمكن للتطبيق إرسال إشارة إضافية حول أدائه ومواعيده النهائية. يساعد ذلك النظام في زيادة الأداء بشكل أكثر فعالية (تحسين الأداء) وخفض سرعة الساعة بسرعة عند اكتمال عبء العمل (توفير استهلاك الطاقة).

سرعة الساعة

عندما تضبط أجهزة Android سرعة ساعة وحدة المعالجة المركزية بشكل ديناميكي، يمكن أن يؤثر التردد في أداء الرمز البرمجي. يُعد تصميم رمز برمجي يعالج سرعات الساعة الديناميكية أمرًا مهمًا لزيادة الأداء إلى أقصى حد والحفاظ على حالة حرارية آمنة واستخدام الطاقة بكفاءة. لا يمكنك تخصيص ترددات وحدة المعالجة المركزية مباشرةً في رمز تطبيقك. نتيجةً لذلك، تتمثل إحدى الطرق الشائعة التي تحاول التطبيقات من خلالها التشغيل بسرعات ساعة أعلى لوحدة المعالجة المركزية في تشغيل حلقة مشغولة في سلسلة محادثات في الخلفية لجعل عبء العمل يبدو أكثر تطلبًا. هذه ممارسة سيئة لأنها تهدر الطاقة وتزيد الحمل الحراري على الجهاز عندما لا يستخدم التطبيق فعليًا الموارد الإضافية. تم تصميم واجهة برمجة التطبيقات PerformanceHint لوحدة المعالجة المركزية لمعالجة هذه المشكلة. من خلال إعلام النظام بمدة العمل الفعلية ومدة العمل المستهدفة، سيتمكّن Android من الحصول على نظرة عامة على احتياجات التطبيق لوحدة المعالجة المركزية وتخصيص الموارد بكفاءة. سيؤدي ذلك إلى تحقيق الأداء الأمثل عند مستوى استهلاك الطاقة الفعّال.

أنواع النواة

تُعد أنواع نواة وحدة المعالجة المركزية التي يتم تشغيل تطبيقك عليها عاملاً مهمًا آخر في الأداء. غالبًا ما تغيّر أجهزة Android نواة وحدة المعالجة المركزية المخصّصة لسلسلة محادثات بشكل ديناميكي استنادًا إلى سلوك عبء العمل الأخير. يكون تخصيص نواة وحدة المعالجة المركزية أكثر تعقيدًا على أنظمة SoC التي تتضمّن أنواعًا متعددة من النواة. في بعض هذه الأجهزة، لا يمكن استخدام النواة الأكبر إلا لفترة وجيزة بدون الانتقال إلى حالة غير مستدامة حراريًا.

يجب ألا يحاول تطبيقك ضبط تقارب نواة وحدة المعالجة المركزية للأسباب التالية:

  • يختلف أفضل نوع نواة لعبء العمل باختلاف طراز الجهاز.
  • يختلف مدى استدامة تشغيل النواة الأكبر باختلاف نظام SoC والحلول الحرارية المختلفة التي يقدّمها كل طراز جهاز.
  • يمكن أن يؤدي التأثير البيئي على الحالة الحرارية إلى زيادة تعقيد اختيار النواة. على سبيل المثال، يمكن أن يؤدي الطقس أو جراب الهاتف إلى تغيير الحالة الحرارية للجهاز.
  • لا يمكن أن يستوعب اختيار النواة الأجهزة الجديدة التي تتضمّن إمكانات إضافية للأداء والحرارة. نتيجةً لذلك، غالبًا ما تتجاهل الأجهزة تقارب المعالج في التطبيق.

مثال على السلوك التلقائي لمجدول Linux

سلوك برنامج جدولة Linux
الشكل 1. يمكن أن يستغرق المنظّم حوالي 200 ملي ثانية لزيادة أو خفض تردد وحدة المعالجة المركزية. تعمل ADPF مع نظام "تغيير الجهد والتردد الديناميكي" (DVFS) لتقديم أفضل أداء لكل واط

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

ADPF Abstracts more than DVFS Latencies
الشكل 2. تعرف ADPF كيفية اتخاذ أفضل قرار نيابةً عنك.
  • إذا كانت المهام بحاجة إلى التشغيل على وحدة معالجة مركزية معيّنة، تعرف واجهة برمجة التطبيقات PerformanceHint كيفية اتخاذ هذا القرار نيابةً عنك.
  • لذلك، ليس عليك استخدام التقارب.
  • تتضمّن الأجهزة طوبولوجيات مختلفة، وتختلف خصائص الطاقة والخصائص الحرارية بشكل كبير بحيث لا يمكن عرضها لمطوّر التطبيق.
  • لا يمكنك وضع أي افتراضات حول النظام الأساسي الذي تستخدمه.

الحل

توفر ADPF الفئة PerformanceHintManager حتى تتمكن التطبيقات من إرسال تلميحات الأداء إلى Android لسرعة ساعة وحدة المعالجة المركزية و نوع النواة. يمكن لنظام التشغيل بعد ذلك تحديد أفضل طريقة لاستخدام التلميحات استنادًا إلى نظام SoC والحل الحراري للجهاز. إذا كان تطبيقك يستخدم واجهة برمجة التطبيقات هذه إلى جانب رصد الحالة الحرارية، يمكنه تقديم تلميحات أكثر استنارة إلى نظام تشغيل بدلاً من استخدام الحلقات المشغولة وتقنيات الترميز الأخرى التي يمكن أن تؤدي إلى تقييد.

إليك كيفية تطبيق النظرية:

تهيئة 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);