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

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

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

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

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

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

توجد الواجهات المتسلسلة الخاصة بـ HAL للشبكات العصبية في hardware/interfaces/neuralnetworks/1.2/ وهي موضحة أدناه. لمزيد من المعلومات عن واجهات البث المفاجئ في Ndk layer، يُرجى الاطّلاع على 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 هي استخدام دالة utilty burst‏ ::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();
}