Neural Networks HAL 1.2 ขอแนะนำแนวคิดเรื่องการดำเนินการแบบเบิร์สต์ การดำเนินการแบบเบิร์สต์คือลำดับการดำเนินการของโมเดลที่เตรียมไว้เดียวกันซึ่งเกิดขึ้นอย่างรวดเร็วต่อเนื่องกัน เช่น การดำเนินการกับเฟรมของการจับภาพจากกล้องหรือตัวอย่างเสียงที่ต่อเนื่องกัน ออบเจ็กต์แบบเบิร์สต์ใช้เพื่อควบคุมชุดการดำเนินการแบบเบิร์สต์ และเพื่อเก็บรักษาทรัพยากรระหว่างการดำเนินการ ซึ่งช่วยให้การดำเนินการมีค่าใช้จ่ายต่ำลง ออบเจ็กต์แบบเบิร์สต์ช่วยให้การเพิ่มประสิทธิภาพ 3 อย่างต่อไปนี้เป็นไปได้
- ระบบจะสร้างออบเจ็กต์แบบเบิร์สต์ก่อนลำดับการดำเนินการ และปล่อยออบเจ็กต์เมื่อลำดับการดำเนินการสิ้นสุดลง ด้วยเหตุนี้ อายุการใช้งานของออบเจ็กต์แบบเบิร์สต์จึงเป็นคำแนะนำให้ไดรเวอร์ทราบว่าควรอยู่ในสถานะที่มีประสิทธิภาพสูงนานเท่าใด
- ออบเจ็กต์แบบเบิร์สต์สามารถเก็บรักษาทรัพยากรระหว่างการดำเนินการได้ ตัวอย่างเช่น ไดรเวอร์สามารถแมปออบเจ็กต์หน่วยความจำในการดำเนินการครั้งแรก และแคชการแมปในออบเจ็กต์แบบเบิร์สต์เพื่อนำไปใช้ซ้ำในการดำเนินการครั้งต่อๆ ไป ระบบจะปล่อยทรัพยากรที่แคชไว้เมื่อทำลายออบเจ็กต์แบบเบิร์สต์ หรือเมื่อรันไทม์ NNAPI แจ้งให้ออบเจ็กต์แบบเบิร์สต์ทราบว่าไม่จำเป็นต้องใช้ทรัพยากรนั้นอีกต่อไป
- ออบเจ็กต์แบบเบิร์สต์ใช้ คิวข้อความด่วน (FMQ) เพื่อสื่อสารระหว่างกระบวนการของแอปและไดรเวอร์ ซึ่งช่วยลดเวลาในการตอบสนองได้เนื่องจาก FMQ จะข้าม HIDL และส่งข้อมูลไปยังกระบวนการอื่นโดยตรงผ่าน FIFO แบบวงกลมแบบอะตอมมิกในหน่วยความจำที่แชร์ กระบวนการผู้ใช้จะทราบว่าจะนำรายการออกจากคิวและเริ่มประมวลผลโดยการโพลจำนวนองค์ประกอบใน FIFO หรือโดยการรอแฟล็กเหตุการณ์ของ FMQ ซึ่งผู้ผลิตส่งสัญญาณ แฟล็กเหตุการณ์นี้คือมิวเท็กซ์พื้นที่ผู้ใช้ด่วน (futex)
FMQ เป็นโครงสร้างข้อมูลระดับต่ำที่ไม่มีการรับประกันอายุการใช้งานในกระบวนการต่างๆ และไม่มีกลไกในตัวสำหรับตรวจสอบว่ากระบวนการที่ปลายอีกด้านของ FMQ ทำงานตามที่คาดไว้หรือไม่ ดังนั้น หากผู้ผลิตสำหรับ FMQ หยุดทำงาน ผู้ใช้ก็อาจติดอยู่กับการรอข้อมูลที่ไม่เคยมาถึง วิธีแก้ปัญหาหนึ่งคือให้ไดรเวอร์เชื่อมโยง FMQ กับออบเจ็กต์แบบเบิร์สต์ระดับสูงกว่าเพื่อตรวจหาเมื่อการดำเนินการแบบเบิร์สต์สิ้นสุดลง
เนื่องจากการดำเนินการแบบเบิร์สต์ทำงานกับอาร์กิวเมนต์เดียวกันและแสดงผลลัพธ์เดียวกันกับเส้นทางการดำเนินการอื่นๆ FMQ พื้นฐานจึงต้องส่งข้อมูลเดียวกันไปยังและจากไดรเวอร์บริการ NNAPI อย่างไรก็ตาม FMQ สามารถโอนได้เฉพาะประเภทข้อมูลธรรมดา การโอนข้อมูลที่ซับซ้อนทำได้โดยการซีเรียลไลซ์และดีซีเรียลไลซ์บัฟเฟอร์ที่ซ้อนกัน (ประเภทเวกเตอร์) โดยตรงใน FMQ และใช้คอลแบ็กออบเจ็กต์ HIDL เพื่อโอนแฮนเดิลพูลหน่วยความจำตามความต้องการ ด้านผู้ผลิต
ของ FMQ ต้องส่งข้อความคำขอหรือข้อความผลลัพธ์ไปยังผู้ใช้
แบบอะตอมมิกโดยใช้ MessageQueue::writeBlocking หากคิวบล็อก หรือ
โดยใช้ MessageQueue::write หากคิวไม่บล็อก
อินเทอร์เฟซแบบเบิร์สต์
อินเทอร์เฟซแบบเบิร์สต์สำหรับ Neural Networks 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 ที่อยู่ในบริการ Neural Networks
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
ฟังก์ชันโรงงานนี้มีการโอเวอร์โหลด 2 รายการ
- การโอเวอร์โหลดรายการหนึ่งยอมรับตัวชี้ไปยังออบเจ็กต์
IPreparedModelฟังก์ชันยูทิลิตีนี้ใช้เมธอดexecuteSynchronouslyในออบเจ็กต์IPreparedModelเพื่อดำเนินการโมเดล - การโอเวอร์โหลดรายการหนึ่งยอมรับออบเจ็กต์
IBurstExecutorWithCacheที่ปรับแต่งได้ ซึ่งใช้เพื่อแคชทรัพยากร (เช่น การแมปhidl_memory) ที่คงอยู่ในการดำเนินการหลายรายการ
การโอเวอร์โหลดแต่ละรายการจะแสดงผลออบเจ็กต์ IBurstContext (ซึ่งแสดงถึงออบเจ็กต์แบบเบิร์สต์) ที่มีและจัดการเธรด Listener เฉพาะของตัวเอง เธรดนี้จะรับคำขอจาก FMQ requestChannel ดำเนินการอนุมาน แล้วแสดงผลลัพธ์ผ่าน FMQ resultChannel ระบบจะปล่อยเธรดนี้และทรัพยากรอื่นๆ ทั้งหมดที่มีอยู่ในออบเจ็กต์ IBurstContext โดยอัตโนมัติเมื่อไคลเอ็นต์ของเบิร์สต์สูญเสียการอ้างอิงถึง IBurstContext
หรือคุณจะสร้างการติดตั้งใช้งาน IBurstContext ของคุณเองที่เข้าใจวิธีส่งและรับข้อความผ่าน FMQ requestChannel และ resultChannel ที่ส่งไปยัง 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();
}