หน้านี้อธิบายโครงสร้างข้อมูลและวิธีการที่ใช้ในการสื่อสารบัฟเฟอร์ตัวถูกดำเนินการระหว่างไดรเวอร์กับเฟรมเวิร์กอย่างมีประสิทธิภาพ
เมื่อคอมไพล์โมเดล เฟรมเวิร์กจะระบุค่าของตัวถูกดำเนินการคงที่ ให้กับไดรเวอร์ ค่าของตัวถูกดำเนินการคงที่จะอยู่ในเวกเตอร์ HIDL หรือพูลหน่วยความจำที่ใช้ร่วมกัน ทั้งนี้ขึ้นอยู่กับอายุการใช้งานของตัวถูกดำเนินการคงที่
- หากอายุการใช้งานเป็น
CONSTANT_COPY
ค่าจะอยู่ในฟิลด์operandValues
ของโครงสร้างโมเดล เนื่องจากระบบจะคัดลอกค่าในเวกเตอร์ HIDL ระหว่างการสื่อสารระหว่างกระบวนการ (IPC) โดยทั่วไปจึงใช้เพื่อเก็บข้อมูลจำนวนเล็กน้อยเท่านั้น เช่น ตัวถูกดำเนินการแบบสเกลาร์ (เช่น สเกลาร์การเปิดใช้งานในADD
) และพารามิเตอร์เทนเซอร์ขนาดเล็ก (เช่น เทนเซอร์รูปร่างในRESHAPE
) - หากอายุการใช้งานเป็น
CONSTANT_REFERENCE
ค่าจะอยู่ในฟิลด์pools
ของโครงสร้างโมเดล เฉพาะแฮนเดิลของพูลหน่วยความจำที่ใช้ร่วมกันเท่านั้นที่จะซ้ำกันระหว่าง IPC แทนที่จะคัดลอกค่าดิบ ดังนั้น การเก็บข้อมูลจำนวนมาก (เช่น พารามิเตอร์น้ำหนักในการผัน) โดยใช้พูลหน่วยความจำที่ใช้ร่วมกันจึงมีประสิทธิภาพมากกว่าเวกเตอร์ HIDL
เมื่อถึงเวลาดำเนินการโมเดล เฟรมเวิร์กจะจัดเตรียมบัฟเฟอร์ของตัวถูกดำเนินการอินพุตและเอาต์พุตให้กับไดรเวอร์ ข้อมูลอินพุตและเอาต์พุตของการดำเนินการจะสื่อสารผ่านคอลเล็กชันของพูลหน่วยความจำเสมอ ซึ่งต่างจากค่าคงที่ในเวลาคอมไพล์ที่อาจส่งในเวกเตอร์ HIDL
ระบบใช้ประเภทข้อมูล HIDL hidl_memory
ทั้งในการคอมไพล์และการดำเนินการเพื่อ
แสดงถึงพูลหน่วยความจำที่ใช้ร่วมกันซึ่งไม่ได้แมป ไดรเวอร์ควรแมปหน่วยความจำ
ตามนั้นเพื่อให้ใช้งานได้ตามชื่อของhidl_memory
ประเภทข้อมูล
ชื่อหน่วยความจำที่รองรับมีดังนี้
ashmem
: ความทรงจำที่มีร่วมกันของ Android ดูรายละเอียดเพิ่มเติมได้ที่หน่วยความจำmmap_fd
: หน่วยความจำที่ใช้ร่วมกันซึ่งได้รับการสนับสนุนโดยตัวอธิบายไฟล์ผ่านmmap
hardware_buffer_blob
: ความทรงจำที่แชร์ซึ่งได้รับการสนับสนุนจาก AHardwareBuffer ที่มีรูปแบบAHARDWARE_BUFFER_FORMAT_BLOB
พร้อมใช้งานจาก Neural Networks (NN) HAL 1.2 ดูรายละเอียดเพิ่มเติมได้ที่ AHardwareBufferhardware_buffer
: หน่วยความจำที่ใช้ร่วมกันซึ่งมี AHardwareBuffer ทั่วไปเป็นข้อมูลสำรองซึ่งไม่ได้ใช้รูปแบบAHARDWARE_BUFFER_FORMAT_BLOB
บัฟเฟอร์ฮาร์ดแวร์ในโหมดที่ไม่ใช่ BLOB รองรับเฉพาะในการดำเนินการโมเดลเท่านั้น โดยพร้อมใช้งานจาก NN HAL 1.2 ดูรายละเอียดเพิ่มเติมได้ที่ AHardwareBuffer
ตั้งแต่ NN HAL 1.3 เป็นต้นไป NNAPI จะรองรับโดเมนหน่วยความจำที่จัดเตรียมอินเทอร์เฟซตัวจัดสรร สำหรับบัฟเฟอร์ที่ไดรเวอร์จัดการ บัฟเฟอร์ที่ไดรเวอร์จัดการยังใช้เป็นอินพุตหรือเอาต์พุตการดำเนินการได้ด้วย โปรดดูรายละเอียดเพิ่มเติมที่โดเมน หน่วยความจำ
ไดรเวอร์ NNAPI ต้องรองรับการแมปชื่อหน่วยความจำ ashmem
และ mmap_fd
ตั้งแต่ NN HAL 1.3 เป็นต้นไป ไดรเวอร์ต้องรองรับการแมป hardware_buffer_blob
ด้วย การรองรับ
โหมดทั่วไปที่ไม่ใช่ BLOB hardware_buffer
และโดเมนหน่วยความจำเป็นค่าที่ไม่บังคับ
AHardwareBuffer
HardwareBuffer เป็นหน่วยความจำที่ใช้ร่วมกันประเภทหนึ่งซึ่งครอบบัฟเฟอร์ Gralloc ใน Android 10, Neural Networks API (NNAPI) รองรับการใช้ AHardwareBuffer
ซึ่งช่วยให้ไดรเวอร์ดำเนินการได้โดยไม่ต้องคัดลอกข้อมูล ซึ่งจะช่วยปรับปรุง
ประสิทธิภาพและการใช้พลังงานของแอป ตัวอย่างเช่น สแต็ก HAL ของกล้อง
สามารถส่งออบเจ็กต์ AHardwareBuffer ไปยัง NNAPI สำหรับภาระงานแมชชีนเลิร์นนิง
โดยใช้แฮนเดิล AHardwareBuffer ที่สร้างขึ้นโดย NDK ของกล้องและ NDK ของสื่อ
API ดูข้อมูลเพิ่มเติมได้ที่
ANeuralNetworksMemory_createFromAHardwareBuffer
ระบบจะส่งออบเจ็กต์ HardwareBuffer ที่ใช้ใน NNAPI ไปยังไดรเวอร์ผ่าน
hidl_memory
struct ที่ชื่อ hardware_buffer
หรือ hardware_buffer_blob
hidl_memory
struct hardware_buffer_blob
แสดงเฉพาะออบเจ็กต์ AHardwareBuffer
ที่มีรูปแบบ AHARDWAREBUFFER_FORMAT_BLOB
ข้อมูลที่เฟรมเวิร์กต้องการจะได้รับการเข้ารหัสในฟิลด์ hidl_handle
ของโครงสร้าง hidl_memory
ฟิลด์ hidl_handle
จะรวม native_handle
,
ซึ่งเข้ารหัสข้อมูลเมตาที่จำเป็นทั้งหมดเกี่ยวกับ AHardwareBuffer หรือบัฟเฟอร์ Gralloc
ไดรเวอร์ต้องถอดรหัสฟิลด์ hidl_handle
ที่ระบุอย่างถูกต้องและเข้าถึงหน่วยความจำที่อธิบายโดย hidl_handle
เมื่อเรียกใช้เมธอด getSupportedOperations_1_2
,
getSupportedOperations_1_1
หรือ getSupportedOperations
ไดรเวอร์ควรตรวจหาว่าสามารถถอดรหัส hidl_handle
ที่ระบุและเข้าถึงหน่วยความจำที่อธิบายโดย hidl_handle
ได้หรือไม่ การเตรียมโมเดลต้องล้มเหลวหากไม่รองรับฟิลด์ hidl_handle
ที่ใช้สำหรับตัวถูกดำเนินการคงที่ การดำเนินการต้องล้มเหลวหากระบบไม่รองรับฟิลด์ hidl_handle
ที่ใช้สำหรับ
ตัวถูกดำเนินการอินพุตหรือเอาต์พุตของการดำเนินการ ขอแนะนำ
ให้คนขับส่งรหัสข้อผิดพลาด GENERAL_FAILURE
หากการเตรียมโมเดล
หรือการดำเนินการล้มเหลว
โดเมนหน่วยความจำ
สำหรับอุปกรณ์ที่ใช้ Android 11 ขึ้นไป NNAPI รองรับโดเมนหน่วยความจำที่จัดเตรียมอินเทอร์เฟซตัวจัดสรรสำหรับบัฟเฟอร์ที่ไดรเวอร์จัดการ ซึ่งช่วยให้ส่งหน่วยความจำดั้งเดิมของอุปกรณ์ในการดำเนินการต่างๆ ได้ โดยไม่ต้องคัดลอกและแปลงข้อมูลที่ไม่จำเป็นระหว่างการดำเนินการต่อเนื่อง ในไดรเวอร์เดียวกัน ภาพที่ 1 แสดงขั้นตอนการทำงานนี้
รูปที่ 1 บัฟเฟอร์การไหลของข้อมูลโดยใช้โดเมนหน่วยความจำ
ฟีเจอร์โดเมนหน่วยความจำมีไว้สำหรับเทนเซอร์ที่ส่วนใหญ่เป็นภายในไดรเวอร์และไม่จำเป็นต้องเข้าถึงบ่อยๆ ในฝั่งไคลเอ็นต์ ตัวอย่างของเทนเซอร์ดังกล่าว ได้แก่ เทนเซอร์สถานะในโมเดลลำดับ สำหรับเทนเซอร์ที่ต้องเข้าถึง CPU บ่อยครั้งในฝั่งไคลเอ็นต์ ขอแนะนำให้ใช้พูลหน่วยความจำที่ใช้ร่วมกัน
หากต้องการรองรับฟีเจอร์โดเมนหน่วยความจำ ให้ใช้
IDevice::allocate
เพื่อให้เฟรมเวิร์กขอการจัดสรรบัฟเฟอร์ที่ไดรเวอร์จัดการได้ ในระหว่างการจัดสรร เฟรมเวิร์กจะระบุพร็อพเพอร์ตี้และการใช้งานต่อไปนี้สำหรับบัฟเฟอร์
BufferDesc
อธิบายพร็อพเพอร์ตี้ที่จำเป็นของบัฟเฟอร์BufferRole
อธิบายรูปแบบการใช้งานที่เป็นไปได้ของบัฟเฟอร์เป็นอินพุตหรือเอาต์พุต ของโมเดลที่เตรียมไว้ คุณระบุบทบาทได้หลายบทบาทในระหว่างการจัดสรรบัฟเฟอร์ และใช้บัฟเฟอร์ที่จัดสรรได้เฉพาะบทบาทที่ระบุ
บัฟเฟอร์ที่จัดสรรอยู่ภายในไดรเวอร์ ไดรเวอร์สามารถเลือกบัฟเฟอร์
ตำแหน่งหรือเลย์เอาต์ข้อมูลใดก็ได้ เมื่อจัดสรรบัฟเฟอร์เรียบร้อยแล้ว
ไคลเอ็นต์ของไดรเวอร์จะอ้างอิงหรือโต้ตอบกับบัฟเฟอร์ได้โดยใช้
โทเค็นที่ส่งคืนหรือออบเจ็กต์ IBuffer
ระบบจะระบุโทเค็นจาก IDevice::allocate
เมื่ออ้างอิงบัฟเฟอร์เป็นหนึ่งในออบเจ็กต์ MemoryPool
ในโครงสร้าง Request
ของการดำเนินการ ไดรเวอร์ต้องใช้การตรวจสอบที่เหมาะสมทุกครั้งที่มีการใช้บัฟเฟอร์เพื่อป้องกันไม่ให้กระบวนการพยายามเข้าถึงบัฟเฟอร์ที่จัดสรรในกระบวนการอื่น ไดรเวอร์ต้องตรวจสอบว่าการใช้บัฟเฟอร์เป็นหนึ่งในBufferRole
บทบาทที่ระบุไว้ในระหว่างการจัดสรร และต้องหยุดการดำเนินการทันทีหากการใช้งานไม่ถูกต้อง
ออบเจ็กต์
IBuffer
ใช้สำหรับการคัดลอกหน่วยความจำอย่างชัดเจน ในบางกรณี ไคลเอ็นต์ของ
ไดรเวอร์ต้องเริ่มต้นบัฟเฟอร์ที่ไดรเวอร์จัดการจากพูลหน่วยความจำที่แชร์
หรือคัดลอกบัฟเฟอร์ไปยังพูลหน่วยความจำที่แชร์ ตัวอย่างกรณีการใช้งานมีดังนี้
- การเริ่มต้นเทนเซอร์สถานะ
- การแคชผลลัพธ์กลาง
- การดำเนินการสำรองใน CPU
หากต้องการรองรับกรณีการใช้งานเหล่านี้ ไดรเวอร์ต้องใช้
IBuffer::copyTo
และ
IBuffer::copyFrom
กับ ashmem
, mmap_fd
และ hardware_buffer_blob
หากรองรับการจัดสรรโดเมนหน่วยความจำ ไดรเวอร์จะรองรับโหมดที่ไม่ใช่ BLOB หรือไม่ก็ได้
hardware_buffer
ในระหว่างการจัดสรรบัฟเฟอร์ ระบบจะอนุมานมิติข้อมูลของบัฟเฟอร์จาก
ตัวถูกดำเนินการของโมเดลที่สอดคล้องกันของบทบาททั้งหมดที่ระบุโดย BufferRole
และ
มิติข้อมูลที่ระบุใน BufferDesc
เมื่อรวมข้อมูลมิติทั้งหมดเข้าด้วยกัน บัฟเฟอร์อาจมีมิติข้อมูลหรืออันดับที่ไม่รู้จัก ในกรณีดังกล่าว บัฟเฟอร์จะอยู่ในสถานะที่ยืดหยุ่นซึ่งมีมิติข้อมูลคงที่เมื่อใช้เป็นอินพุตของโมเดล และอยู่ในสถานะแบบไดนามิกเมื่อใช้เป็นเอาต์พุตของโมเดล คุณสามารถใช้บัฟเฟอร์เดียวกันกับเอาต์พุตที่มีรูปร่างต่างกันในการดำเนินการต่างๆ และไดรเวอร์ต้องจัดการการปรับขนาดบัฟเฟอร์อย่างเหมาะสม
โดเมนหน่วยความจำเป็นฟีเจอร์เสริม ไดรเวอร์อาจพิจารณาว่าไม่สามารถ รองรับคำขอการจัดสรรที่ระบุได้เนื่องจากเหตุผลหลายประการ เช่น
- บัฟเฟอร์ที่ขอมีขนาดแบบไดนามิก
- ไดรเวอร์มีข้อจำกัดด้านหน่วยความจำที่ทำให้ไม่สามารถจัดการบัฟเฟอร์ขนาดใหญ่ได้
หลายเธรดอาจอ่านจากบัฟเฟอร์ที่ไดรเวอร์จัดการพร้อมกันได้ การเข้าถึงบัฟเฟอร์พร้อมกันเพื่อเขียนหรืออ่าน/เขียน ไม่ได้รับการกำหนด แต่ต้องไม่ทำให้บริการไดรเวอร์ขัดข้องหรือบล็อกผู้เรียก อย่างไม่มีกำหนด ไดรเวอร์อาจแสดงข้อผิดพลาดหรือปล่อยให้เนื้อหาของบัฟเฟอร์อยู่ในสถานะ ที่ไม่แน่นอน