تقدّم طبقة تجريد الأجهزة (HAL) 1.2 للشبكات العصبية مفهوم عمليات التنفيذ المتسلسلة. عمليات التنفيذ المتسلسلة هي تسلسل لعمليات تنفيذ النموذج المُعدّ نفسه تحدث بتتابع سريع، مثل العمليات التي تتم على لقطات من كاميرا أو عيّنات صوتية متتالية. يُستخدَم عنصر التنفيذ المتسلسل للتحكّم في مجموعة من عمليات التنفيذ المتسلسل، وللحفاظ على الموارد بين عمليات التنفيذ، ما يتيح خفض النفقات العامة لعمليات التنفيذ. تتيح عناصر Burst إجراء ثلاث عمليات تحسين:
- يتم إنشاء عنصر burst قبل سلسلة من عمليات التنفيذ، ويتم تحريره عند انتهاء السلسلة. لهذا السبب، تشير مدة صلاحية عنصر burst إلى برنامج التشغيل إلى المدة التي يجب أن يظل فيها في حالة عالية الأداء.
- يمكن لكائن الدفعات الحفاظ على الموارد بين عمليات التنفيذ. على سبيل المثال، يمكن للسائق ربط عنصر ذاكرة عند التنفيذ الأول وتخزين الربط مؤقتًا في عنصر الحزمة لإعادة استخدامه في عمليات التنفيذ اللاحقة. يمكن إطلاق أي مورد مخزّن مؤقتًا عند إيقاف العنصر السريع أو عندما يُعلم وقت تشغيل NNAPI العنصر السريع بأنّ المورد لم يعُد مطلوبًا.
- يستخدم عنصر Burst قوائم انتظار الرسائل السريعة (FMQ) للتواصل بين عمليات التطبيق وعمليات برنامج التشغيل. ويمكن أن يؤدي ذلك إلى تقليل وقت الاستجابة لأنّ FMQ يتجاوز HIDL وينقل البيانات مباشرةً إلى عملية أخرى من خلال قائمة انتظار FIFO دائرية ذرية في الذاكرة المشتركة. تعرف عملية المستهلك كيفية إزالة عنصر من قائمة الانتظار وبدء معالجته إما عن طريق استطلاع عدد العناصر في قائمة FIFO أو عن طريق انتظار علامة حدث FMQ التي يشير إليها المنتج. علامة الحدث هذه هي عبارة عن قفل استبعاد متبادل سريع في مساحة المستخدم (futex).
وFMQ هي بنية بيانات منخفضة المستوى لا تقدّم أي ضمانات بشأن مدة بقائها صالحة في جميع العمليات، كما لا تتضمّن آلية مدمجة لتحديد ما إذا كانت العملية على الطرف الآخر من FMQ تعمل على النحو المتوقّع. نتيجةً لذلك، إذا توقّف منتج FMQ عن العمل، قد يضطر المستهلك إلى الانتظار إلى الأبد للحصول على البيانات. أحد حلول هذه المشكلة هو أن يربط برنامج التشغيل FMQ بكائن الدفعات الأعلى مستوى لرصد وقت انتهاء تنفيذ الدفعات.
وبما أنّ عمليات التنفيذ السريع تعمل على الوسيطات نفسها وتعرض النتائج نفسها كمسارات التنفيذ الأخرى، يجب أن تمرّر قوائم FMQ الأساسية البيانات نفسها إلى برامج تشغيل خدمة NNAPI ومنها. ومع ذلك، يمكن أن تنقل FMQ أنواع البيانات العادية فقط. يتم نقل البيانات المعقّدة من خلال نشرها على نحو متسلسِل وإلغاء تسلسلها في المخازن المؤقتة المتداخلة (أنواع المتجهات) مباشرةً في قوائم انتظار الرسائل السريعة (FMQ)، واستخدام عناصر معاودة الاتصال في HIDL لنقل معرّفات مجموعة الذاكرة عند الطلب. يجب أن يرسل جانب المنتج في قائمة انتظار الرسائل السريعة (FMQ) رسائل الطلبات أو النتائج إلى المستهلك بشكل ذري باستخدام MessageQueue::writeBlocking
إذا كانت قائمة الانتظار تحظر، أو باستخدام MessageQueue::write
إذا كانت قائمة الانتظار لا تحظر.
واجهات الصور المتسلسلة
يمكن العثور على واجهات نقل البيانات السريع الخاصة بطبقة HAL للشبكات العصبية في
hardware/interfaces/neuralnetworks/1.2/
وهي موضّحة أدناه. لمزيد من المعلومات حول واجهات النقل السريع للبيانات في طبقة NDK، يُرجى الاطّلاع على
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 لمعاودة الاتصال الذي تم إنشاؤه بواسطة وقت تشغيل
الشبكات العصبونية ويستخدمه نظام الشبكات العصبونية لاسترداد عناصر hidl_memory
المقابلة لمعرّفات الخانات.
- IBurstCallback: كائن رد الاتصال الذي تستخدمه الخدمة لاسترداد كائنات الذاكرة.
IPreparedModel.hal
تمت إضافة IPreparedModel.hal
في الإصدار 1.2 من طبقة تجريد الأجهزة (HAL) مع طريقة لإنشاء كائن IBurstContext
من نموذج مُعدّ.
-
configureExecutionBurst
: تضبط هذه السمة عنصرًا من عناصر "التنفيذ السريع" يُستخدَم لتنفيذ استنتاجات متعددة على نموذج مُعدّ بسرعة متتالية.
إتاحة عمليات التنفيذ المتسلسلة في برنامج تشغيل
أبسط طريقة لتوفير دعم الكائنات المتسلسلة في خدمة HIDL NNAPI هي استخدام دالة الأداة المساعدة ::android::nn::ExecutionBurstServer::create
، والتي يمكن العثور عليها في ExecutionBurstServer.h
وتكون مضمّنة في المكتبتين الثابتتين libneuralnetworks_common
وlibneuralnetworks_util
. تحتوي دالة المصنع هذه على حمولتين زائدتين:
- يقبل أحد التحميلات الزائدة مؤشرًا إلى عنصر
IPreparedModel
. تستخدم دالة الأداة المساعدة هذه طريقةexecuteSynchronously
في كائنIPreparedModel
لتنفيذ النموذج. - يقبل أحد التحميلات الزائدة عنصر
IBurstExecutorWithCache
قابلاً للتخصيص، ويمكن استخدامه لتخزين الموارد مؤقتًا (مثل عمليات ربطhidl_memory
) التي تستمر في عمليات التنفيذ المتعددة.
تعرض كل عملية تحميل زائد كائن IBurstContext
(الذي يمثّل كائن burst) يحتوي على سلسلة تعليمات خاصة بالمستمع ويديرها. يتلقّى هذا السلسلة طلبات من 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();
}