การดำเนินการแบบทันทีและคิวข้อความที่รวดเร็ว

โครงข่ายระบบประสาทเทียม HAL 1.2 นำเสนอแนวคิดของการเรียกใช้แบบทันที การดำเนินการแบบต่อเนื่องเป็นลำดับการดำเนินการของโมเดลที่เตรียมไว้เดียวกันซึ่งเกิดขึ้นต่อเนื่องกันอย่างรวดเร็ว เช่น การดำเนินการในเฟรมของกล้องจับภาพหรือตัวอย่างเสียงที่สำเร็จ ออบเจ็กต์ภาพถ่ายอัจฉริยะใช้เพื่อควบคุมชุดของการถ่ายภาพอัจฉริยะและเพื่อเก็บรักษาทรัพยากรระหว่างการดำเนินการ ซึ่งทำให้การดำเนินการดังกล่าวมีโอเวอร์เฮดต่ำกว่าได้ วัตถุที่ถ่ายต่อเนื่องกันทำให้สามารถเพิ่มประสิทธิภาพได้ 3 แบบ ดังนี้

  1. ออบเจ็กต์ภาพถ่ายอัจฉริยะจะสร้างขึ้นก่อนลำดับการดำเนินการ และพื้นที่ว่างเมื่อลำดับสิ้นสุดลง ด้วยเหตุนี้ อายุการใช้งานของวัตถุระเบิดจึงทำให้ผู้ขับทราบว่าควรอยู่ในสถานะประสิทธิภาพสูงนานเท่าใด
  2. ออบเจ็กต์ต่อเนื่องสามารถเก็บรักษาทรัพยากรระหว่างการดำเนินการได้ ตัวอย่างเช่น ไดรเวอร์สามารถแมปออบเจ็กต์หน่วยความจำในการดำเนินการครั้งแรก และแคชการแมปในออบเจ็กต์ภาพถ่ายอัจฉริยะเพื่อใช้ซ้ำในการดำเนินการครั้งถัดไป ทรัพยากรที่แคชไว้จะปล่อยได้เมื่อออบเจ็กต์ดังกล่าวถูกทำลายหรือเมื่อรันไทม์ของ NNAPI แจ้งเตือนออบเจ็กต์ต่อเนื่องว่าไม่จำเป็นต้องใช้ทรัพยากรดังกล่าวอีกต่อไป
  3. ออบเจ็กต์ภาพถ่ายอัจฉริยะใช้คิวข้อความที่รวดเร็ว (FMQ) เพื่อสื่อสารระหว่างการทำงานของแอปและไดรเวอร์ ซึ่งสามารถลดเวลาในการตอบสนองได้เนื่องจาก FMQ ข้าม HIDL และส่งผ่านข้อมูลไปยังกระบวนการอื่นโดยตรงผ่าน FIFO ทรงกลมอะตอมในหน่วยความจำที่ใช้ร่วมกัน กระบวนการของผู้บริโภคทราบว่าจะต้องยกเลิกการจัดคิวรายการและเริ่มประมวลผลด้วยการหาจำนวนองค์ประกอบใน FIFO หรือโดยการรอแฟล็กเหตุการณ์ของ FMQ ซึ่งได้รับสัญญาณจากผู้ผลิต Flag เหตุการณ์นี้คือ usspace Redirectx (futex) แบบรวดเร็ว

FMQ เป็นโครงสร้างข้อมูลระดับต่ำที่ไม่มีการรับประกันตลอดอายุการใช้งานในกระบวนการต่างๆ และไม่มีกลไกในตัวสำหรับการกำหนดว่ากระบวนการในอีกฝั่งหนึ่งของ FMQ ทำงานได้ตามที่คาดไว้หรือไม่ ดังนั้น หากผู้ผลิต FMQ เสียชีวิต ผู้บริโภคอาจต้องต้องรอข้อมูลที่ยังไม่ได้รับ วิธีแก้ปัญหาหนึ่งของปัญหานี้คือให้คนขับเชื่อมโยง FMQ กับวัตถุระเบิดระดับสูงไว้ เพื่อตรวจจับเมื่อการจับภาพทันทีสิ้นสุดลง

เนื่องจากการดำเนินการแบบทันทีจะทำงานบนอาร์กิวเมนต์เดียวกัน และให้ผลลัพธ์เช่นเดียวกับเส้นทางการดำเนินการอื่นๆ FMQ ที่สำคัญจึงต้องส่งผ่านข้อมูลเดียวกันไปยังและจากไดรเวอร์บริการ NNAPI อย่างไรก็ตาม FMQ จะโอนได้เฉพาะประเภทข้อมูลเก่าเท่านั้น การโอนข้อมูลที่ซับซ้อนสามารถทำได้โดยการทำให้เป็นอนุกรมและดีซีเรียลไลซ์บัฟเฟอร์ที่ซ้อนกัน (ประเภทเวกเตอร์) โดยตรงใน FMQ และใช้ออบเจ็กต์ Callback แบบ 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 สำหรับ Callback ที่สร้างโดยรันไทม์ของโครงข่ายระบบประสาทเทียมและใช้โดยบริการโครงข่ายระบบประสาทเทียมเพื่อเรียกออบเจ็กต์ hidl_memory ที่ตรงกับตัวระบุสล็อต

  • IBurstCallback: ออบเจ็กต์ Callback ที่บริการใช้เพื่อเรียกข้อมูลออบเจ็กต์หน่วยความจำ

IPreparedModel.hal

IPreparedModel.hal มีการขยายใน HAL 1.2 ด้วยวิธีสร้างออบเจ็กต์ IBurstContext จากโมเดลที่จัดเตรียม

  • configureExecutionBurst: กำหนดค่าออบเจ็กต์ภาพถ่ายอัจฉริยะซึ่งใช้เพื่อดำเนินการอนุมานหลายรายการในโมเดลที่เตรียมไว้ต่อกันอย่างรวดเร็ว

รองรับการดำเนินการแบบทันทีในไดรเวอร์

วิธีที่ง่ายที่สุดในการรองรับวัตถุระเบิดในบริการ HIDL NNAPI คือการใช้ฟังก์ชันยูทิลิตีภาพถ่ายอัจฉริยะ ::android::nn::ExecutionBurstServer::create ซึ่งอยู่ใน ExecutionBurstServer.h และจัดแพ็กเกจในไลบรารีแบบคงที่ของ libneuralnetworks_common และ libneuralnetworks_util ฟังก์ชันโรงงานนี้มีโอเวอร์โหลด 2 แบบ ได้แก่

  • โอเวอร์โหลด 1 รายการยอมรับตัวชี้ไปยังออบเจ็กต์ IPreparedModel ฟังก์ชันยูทิลิตีนี้จะใช้เมธอด executeSynchronously ในออบเจ็กต์ IPreparedModel เพื่อเรียกใช้โมเดล
  • โอเวอร์โหลด 1 รายการจะยอมรับออบเจ็กต์ IBurstExecutorWithCache ที่ปรับแต่งได้ ซึ่งสามารถใช้แคชทรัพยากร (เช่น การแมป hidl_memory) แบบถาวรในการดำเนินการหลายรายการได้

โอเวอร์โหลดแต่ละรายการจะแสดงผลออบเจ็กต์ IBurstContext (ซึ่งแทนออบเจ็กต์ภาพถ่ายอัจฉริยะ) ที่มีและจัดการเทรด Listener ของตัวผู้ฟังโดยเฉพาะ เทรดนี้ได้รับคำขอจาก 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();
}