عمليات الإعدام المتفجرة وقوائم انتظار الرسائل السريعة

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

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

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

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

واجهات متفجرة

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

أنواع.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 FMQs التي تم تمريرها إلى 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);

ما يلي هو تطبيق مرجعي لواجهة الاندفاع الموجودة في نموذج برنامج تشغيل Neural Networks في 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();
}