عمليات تنفيذ متسلسلة وقوائم انتظار سريعة للرسائل

يقدّم Neural Networks HAL 1.2 مفهوم عمليات التنفيذ المفاجئة. عمليات تنفيذ المعالجة المكثّفة هي تسلسل عمليات تنفيذ النموذج المعدّ نفسه التي تحدث في تتابع سريع، مثل تلك التي تعمل على لقطات من الكاميرا أو نماذج صوتية متتالية. يُستخدم كائن الصور المتسلسلة للتحكم في مجموعة من عمليات تنفيذ الصور المتسلسلة، وللحفاظ على الموارد بين عمليات التنفيذ، ما يتيح لعمليات التنفيذ خفض النفقات العامة. تتيح لك ميزة "التقاط الصور المتسلسلة" إجراء ثلاثة تحسينات:

  1. يتم إنشاء عنصر معالجة مكثّفة قبل تسلسل عمليات التنفيذ، ويتم تحريره عند انتهاء التسلسل. ولهذا السبب، يشير تاريخ بقاء كائن الصور المتسلسلة إلى برنامج التشغيل إلى المدة التي يجب أن يظل فيها في حالة الأداء العالي.
  2. يمكن لكائن الصور المتسلسلة الاحتفاظ بالموارد بين عمليات التنفيذ. على سبيل المثال، يمكن لبرنامج التشغيل ربط كائن ذاكرة عند التنفيذ الأول وتخزينه مؤقتًا في كائن الصور المتسلسلة لإعادة استخدامه في عمليات التنفيذ اللاحقة. ويمكن تحرير أي مورد مخزَّن مؤقتًا عند إتلاف كائن الصور المتسلسلة أو عندما يُبلغ وقت تشغيل NNAPI الكائن المتسلسلة بأن المورد لم يعد مطلوبًا.
  3. يستخدم كائن الصور المتسلسلة قوائم انتظار الرسائل السريعة (FMQs) للتواصل بين عمليات التطبيق وبرامج التشغيل. وقد يؤدي ذلك إلى تقليل وقت الاستجابة لأن FMQ تتخطى HIDL وتمرر البيانات مباشرةً إلى عملية أخرى من خلال نظام FIFO دائري صغير في الذاكرة المشتركة. يُطلَب من عملية المستهلك إزالة المنتج من قائمة الانتظار وبدء المعالجة إما من خلال استطلاع عدد العناصر في معيار FIFO أو انتظار علامة الفعالية FMQ التي أشار إليها المنتج. علامة الحدث هذه هي قفل ملف ثنائي سريع في مساحة المستخدم (futex).

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

نظرًا لأن عمليات تنفيذ الصور المتسلسلة تعمل على نفس الوسيطات وتعرض النتائج نفسها مثل مسارات التنفيذ الأخرى، يجب أن تمرر FMQs الأساسية البيانات نفسها من وإلى برامج تشغيل خدمة NNAPI. ومع ذلك، لا يمكن لواجهات FMQ نقل سوى أنواع البيانات القديمة. يتم نقل البيانات المعقدة من خلال تسلسل وإلغاء تسلسل المخزن المؤقت المتداخل (أنواع المتجهات) مباشرةً في قوائم الانتظار للطلبات القصيرة (FMQ)، واستخدام عناصر طلب استدعاء HIDL لنقل عناصر ذاكرة التخزين المؤقت عند الطلب. يجب أن يرسل جانب المنتج من FMQ رسائل الطلب أو النتائج إلى المستهلك بشكل كامل باستخدام MessageQueue::writeBlocking إذا كانت قائمة الانتظار محظورة، أو باستخدام MessageQueue::write إذا كانت قائمة الانتظار غير محظورة.

واجهات الصور المتسلسلة

يمكن العثور على واجهات الصور المتسلسلة في HAL للشبكات العصبية في hardware/interfaces/neuralnetworks/1.2/، وهي موضّحة أدناه. لمزيد من المعلومات عن واجهات التصوير المتسلسل في Ndk layer، يُرجى الاطّلاع على frameworks/ml/nn/runtime/include/NeuralNetworks.h.

types.hal

types.hal يحدِّد نوع البيانات التي يتم إرسالها عبر FMQ.

  • FmqRequestDatum: عنصر واحد لتمثيل تسلسلي لشيء تنفيذ Request وقيمته MeasureTiming، ويتم إرساله عبر قائمة انتظار الرسائل السريعة .
  • FmqResultDatum: هو عنصر واحد لعرض متسلسل للقيم الناتجة عن عملية تنفيذ (ErrorStatus وOutputShapes وTiming)، ويتم عرضه من خلال قائمة انتظار الرسائل السريعة.

IBurstContext.hal

IBurstContext.hal يحدِّد عنصر واجهة HIDL الذي يتوفّر في خدمة الشبكات العصبية.

  • IBurstContext: كائن سياق لإدارة موارد الصور المتسلسلة.

IBurstCallback.hal

IBurstCallback.hal يحدِّد عنصر واجهة HIDL لمكالمة إعادة الاتصال التي أنشأها وقت تنفيذ Neural Networks ، وتستخدمها خدمة Neural Networks لاسترداد عناصر hidl_memory المتوافقة مع معرّفات الفتحات.

  • IBurstCallback: عنصر استدعاء تستخدمه الخدمة لاسترداد عناصر الذاكرة.

IPreparedModel.hal

تم توسعة IPreparedModel.hal في HAL 1.2 من خلال طريقة لإنشاء عنصر IBurstContext من نموذج مُعدّ.

  • configureExecutionBurst: يضبط عنصرًا متسلسلًا يُستخدَم لتنفيذ استنتاجات متعدّدة على نموذج مُعدّ بشكل متتابع سريع.

إتاحة عمليات التنفيذ المكثّفة في برنامج تشغيل

إنّ أبسط طريقة لدعم العناصر المتسلسلة في خدمة HIDL NNAPI هي استخدام دالة الأدوات المتسلسلة ::android::nn::ExecutionBurstServer::create، الموجودة في ExecutionBurstServer.h والمجمّعة في المكتبتين libneuralnetworks_common وlibneuralnetworks_util الثابتة. تحتوي دالة المصنع هذه على تحميل زائدَين:

  • يقبل أحد أساليب التحميل الزائد مؤشرًا إلى عنصر IPreparedModel. تستخدم دالة المرافق هذه الطريقة executeSynchronously في عنصر IPreparedModel لتنفيذ النموذج.
  • يقبل أحد أساليب التحميل الزائد عنصر IBurstExecutorWithCache قابل للتخصيص، يمكن استخدامه لتخزين الموارد مؤقتًا (مثل عمليات ربط hidl_memory) التي تظل محفوظة على مدار عمليات تنفيذ متعددة.

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

يمكنك بدلاً من ذلك إنشاء طريقة تنفيذ خاصة بك لـ IBurstContext الذي يفهم كيفية إرسال الرسائل واستلامها باستخدام requestChannel و resultChannel FMQ التي تم تمريرها إلى IPreparedModel::configureExecutionBurst.

يمكن العثور على وظائف الصور المتسلسلة في ExecutionBurstServer.h.

/**
 * Create automated context to manage FMQ-based executions.
 *
 * This function is intended to be used by a service to automatically:
 * 1) Receive data from a provided FMQ
 * 2) Execute a model with the given information
 * 3) Send the result to the created FMQ
 *
 * @param callback Callback used to retrieve memories corresponding to
 *     unrecognized slots.
 * @param requestChannel Input FMQ channel through which the client passes the
 *     request to the service.
 * @param resultChannel Output FMQ channel from which the client can retrieve
 *     the result of the execution.
 * @param executorWithCache Object which maintains a local cache of the
 *     memory pools and executes using the cached memory pools.
 * @result IBurstContext Handle to the burst context.
 */
static sp<ExecutionBurstServer> create(
        const sp<IBurstCallback>& callback, const FmqRequestDescriptor& requestChannel,
        const FmqResultDescriptor& resultChannel,
        std::shared_ptr<IBurstExecutorWithCache> executorWithCache);

/**
 * Create automated context to manage FMQ-based executions.
 *
 * This function is intended to be used by a service to automatically:
 * 1) Receive data from a provided FMQ
 * 2) Execute a model with the given information
 * 3) Send the result to the created FMQ
 *
 * @param callback Callback used to retrieve memories corresponding to
 *     unrecognized slots.
 * @param requestChannel Input FMQ channel through which the client passes the
 *     request to the service.
 * @param resultChannel Output FMQ channel from which the client can retrieve
 *     the result of the execution.
 * @param preparedModel PreparedModel that the burst object was created from.
 *     IPreparedModel::executeSynchronously will be used to perform the
 *     execution.
 * @result IBurstContext Handle to the burst context.
 */
  static sp<ExecutionBurstServer> create(const sp<IBurstCallback>& callback,
                                         const FmqRequestDescriptor& requestChannel,
                                         const FmqResultDescriptor& resultChannel,
                                         IPreparedModel* preparedModel);

في ما يلي تنفيذ مرجعي لواجهة المعالجة المكثّفة للبيانات في ملف رمز المصدر المخصص لبرنامج التشغيل نموذج الشبكات العصبية على الرابط frameworks/ml/nn/driver/sample/SampleDriver.cpp.

Return<void> SamplePreparedModel::configureExecutionBurst(
        const sp<V1_2::IBurstCallback>& callback,
        const MQDescriptorSync<V1_2::FmqRequestDatum>& requestChannel,
        const MQDescriptorSync<V1_2::FmqResultDatum>& resultChannel,
        configureExecutionBurst_cb cb) {
    NNTRACE_FULL(NNTRACE_LAYER_DRIVER, NNTRACE_PHASE_EXECUTION,
                 "SampleDriver::configureExecutionBurst");
    // Alternatively, the burst could be configured via:
    // const sp<V1_2::IBurstContext> burst =
    //         ExecutionBurstServer::create(callback, requestChannel,
    //                                      resultChannel, this);
    //
    // However, this alternative representation does not include a memory map
    // caching optimization, and adds overhead.
    const std::shared_ptr<BurstExecutorWithCache> executorWithCache =
            std::make_shared<BurstExecutorWithCache>(mModel, mDriver, mPoolInfos);
    const sp<V1_2::IBurstContext> burst = ExecutionBurstServer::create(
            callback, requestChannel, resultChannel, executorWithCache);
    if (burst == nullptr) {
        cb(ErrorStatus::GENERAL_FAILURE, {});
    } else {
        cb(ErrorStatus::NONE, burst);
    }
    return Void();
}