اعدام های پشت سر هم و صف های پیام سریع

شبکه های عصبی HAL 1.2 مفهوم اجرای انفجاری را معرفی می کند. اجراهای انفجاری دنباله ای از اجرای همان مدل آماده شده هستند که به سرعت انجام می شوند، مانند مواردی که بر روی فریم های ضبط دوربین یا نمونه های صوتی متوالی عمل می کنند. یک شیء انفجاری برای کنترل مجموعه ای از اجراهای انفجاری و حفظ منابع بین اجراها استفاده می شود و به اجراها امکان می دهد سربار کمتری داشته باشند. اشیاء Burst سه بهینه سازی را فعال می کنند:

  1. یک شی انفجاری قبل از دنباله ای از اجراها ایجاد می شود و با پایان یافتن دنباله آزاد می شود. به همین دلیل، طول عمر جسم انفجاری به راننده نشان می دهد که چه مدت باید در وضعیت با کارایی بالا باقی بماند.
  2. یک شی انفجاری می تواند منابع را بین اجراها حفظ کند. به عنوان مثال، یک راننده می تواند یک شی حافظه را در اولین اجرا نگاشت و نگاشت را در شی burst برای استفاده مجدد در اجرای بعدی ذخیره کند. هر منبع ذخیره شده در حافظه پنهان زمانی که شی انفجاری از بین می رود یا زمانی که زمان اجرا NNAPI به شی انفجاری اطلاع می دهد که منبع دیگر مورد نیاز نیست، آزاد می شود.
  3. یک شی انفجاری از صف‌های پیام سریع (FMQ) برای برقراری ارتباط بین فرآیندهای برنامه و درایور استفاده می‌کند. این می تواند تأخیر را کاهش دهد زیرا FMQ HIDL را دور می زند و داده ها را مستقیماً از طریق یک FIFO دایره ای اتمی در حافظه مشترک به فرآیند دیگری ارسال می کند. فرآیند مصرف‌کننده می‌داند که یک آیتم را در صف قرار داده و پردازش را با نظرسنجی تعداد عناصر موجود در FIFO یا با انتظار بر روی پرچم رویداد FMQ، که توسط تولیدکننده علامت‌گذاری می‌شود، آغاز کند. این پرچم رویداد یک mutex فضای کاربر سریع (futex) است.

یک FMQ یک ساختار داده سطح پایین است که هیچ تضمینی برای طول عمر در سراسر فرآیندها ارائه نمی دهد و هیچ مکانیزم داخلی برای تعیین اینکه آیا فرآیند در انتهای دیگر FMQ مطابق انتظار اجرا می شود یا خیر ندارد. در نتیجه، اگر تولید کننده FMQ بمیرد، مصرف کننده ممکن است منتظر داده هایی باشد که هرگز به دست نمی آیند. یک راه حل برای این مشکل این است که درایور FMQ ها را با شی سطح بالاتر burst مرتبط کند تا تشخیص دهد که چه زمانی اجرای انفجاری به پایان رسیده است.

از آنجا که اجراهای انفجاری بر روی همان آرگومان ها عمل می کنند و نتایج مشابهی را با سایر مسیرهای اجرا برمی گردانند، 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 نوع داده ای را که در FMQ ارسال می شود را تعریف می کند.

  • FmqRequestDatum : یک عنصر واحد از نمایش سریالی یک شی Request اجرا و یک مقدار MeasureTiming که در صف پیام سریع ارسال می شود.
  • FmqResultDatum : یک عنصر واحد از یک نمایش سریالی از مقادیر بازگردانده شده از یک اجرا ( ErrorStatus ، OutputShapes ، و Timing )، که از طریق صف پیام سریع برگردانده می شود.

IBurstContext.hal

IBurstContext.hal شی رابط HIDL را تعریف می کند که در سرویس شبکه های عصبی زندگی می کند.

  • IBurstContext : آبجکت زمینه برای مدیریت منابع یک انفجار.

IBurstCallback.hal

IBurstCallback.hal شئ رابط HIDL را برای یک فراخوان ایجاد شده توسط زمان اجرا شبکه های عصبی تعریف می کند و توسط سرویس شبکه های عصبی برای بازیابی اشیاء hidl_memory مربوط به شناسه های اسلات استفاده می شود.

  • IBurstCallback : شئ Callback که توسط یک سرویس برای بازیابی اشیاء حافظه استفاده می شود.

IPreparedModel.hal

IPreparedModel.hal در HAL 1.2 با روشی برای ایجاد یک شی IBurstContext از یک مدل آماده شده گسترش یافته است.

  • configureExecutionBurst : یک شی انفجاری را که برای اجرای چندین استنتاج بر روی یک مدل آماده شده به صورت متوالی استفاده می شود، پیکربندی می کند.

پشتیبانی از اجرای انفجاری در درایور

ساده ترین راه برای پشتیبانی از اشیاء انفجاری در سرویس HIDL NNAPI، استفاده از تابع ابزار burst ::android::nn::ExecutionBurstServer::create است که در ExecutionBurstServer.h یافت می شود و در libneuralnetworks_common و libneuralnetworks_util می باشد. این عملکرد کارخانه دارای دو بار اضافه است:

  • یک اضافه بار یک اشاره گر به یک شی IPreparedModel را می پذیرد. این تابع ابزار از متد executeSynchronously در یک شی IPreparedModel برای اجرای مدل استفاده می کند.
  • یک بار اضافه، یک شی IBurstExecutorWithCache قابل تنظیم را می پذیرد، که می تواند برای ذخیره منابع (مانند نگاشت های hidl_memory ) که در چندین اجرا باقی می مانند، استفاده شود.

هر اضافه بار یک شی IBurstContext (که نمایانگر شی انفجاری است) را برمی گرداند که رشته شنونده اختصاصی خود را در بر می گیرد و مدیریت می کند. این رشته درخواست ها را از requestChannel FMQ دریافت می کند، استنتاج را انجام می دهد، سپس نتایج را از طریق resultChannel FMQ برمی گرداند. این رشته و سایر منابع موجود در شی IBurstContext به طور خودکار آزاد می شوند زمانی که مشتری burst مرجع خود را به IBurstContext از دست می دهد.

از طرف دیگر، می‌توانید پیاده‌سازی خود را از IBurstContext ایجاد کنید که نحوه ارسال و دریافت پیام‌ها را از طریق requestChannel و FMQs resultChannel به IPreparedModel::configureExecutionBurst ارسال می‌کند.

توابع ابزار burst در 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();
}