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

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

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

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

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

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

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

type.hal

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

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

IBurstContext.hal

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

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

IBurstCallback.hal

تحدّد السمة IBurstCallback.hal كائن واجهة HIDL لاستدعاء الإجراء الذي يتم إنشاؤه بواسطة وقت تشغيل "الشبكات العصبونية" وتستخدمه خدمة "الشبكات العصبونية" لاسترداد كائنات 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 (يمثل كائن الصور المتسلسلة) الذي يحتوي على سلسلة أدوات استماع مخصّصة لها وتديرها. تتلقّى سلسلة المحادثات هذه الطلبات من FMQ requestChannel، وتُجري الاستنتاج، ثم تعرض النتائج من خلال 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();
}