พูลหน่วยความจำ

หน้านี้อธิบายโครงสร้างข้อมูลและวิธีการที่ใช้ในการสื่อสารบัฟเฟอร์ตัวถูกดำเนินการระหว่างไดรเวอร์กับเฟรมเวิร์กอย่างมีประสิทธิภาพ

เมื่อคอมไพล์โมเดล เฟรมเวิร์กจะระบุค่าของตัวถูกดำเนินการคงที่ ให้กับไดรเวอร์ ค่าของตัวถูกดำเนินการคงที่จะอยู่ในเวกเตอร์ 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 ดูรายละเอียดเพิ่มเติมได้ที่ AHardwareBuffer
  • hardware_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 เมื่อรวมข้อมูลมิติทั้งหมดเข้าด้วยกัน บัฟเฟอร์อาจมีมิติข้อมูลหรืออันดับที่ไม่รู้จัก ในกรณีดังกล่าว บัฟเฟอร์จะอยู่ในสถานะที่ยืดหยุ่นซึ่งมีมิติข้อมูลคงที่เมื่อใช้เป็นอินพุตของโมเดล และอยู่ในสถานะแบบไดนามิกเมื่อใช้เป็นเอาต์พุตของโมเดล คุณสามารถใช้บัฟเฟอร์เดียวกันกับเอาต์พุตที่มีรูปร่างต่างกันในการดำเนินการต่างๆ และไดรเวอร์ต้องจัดการการปรับขนาดบัฟเฟอร์อย่างเหมาะสม

โดเมนหน่วยความจำเป็นฟีเจอร์เสริม ไดรเวอร์อาจพิจารณาว่าไม่สามารถ รองรับคำขอการจัดสรรที่ระบุได้เนื่องจากเหตุผลหลายประการ เช่น

  • บัฟเฟอร์ที่ขอมีขนาดแบบไดนามิก
  • ไดรเวอร์มีข้อจำกัดด้านหน่วยความจำที่ทำให้ไม่สามารถจัดการบัฟเฟอร์ขนาดใหญ่ได้

หลายเธรดอาจอ่านจากบัฟเฟอร์ที่ไดรเวอร์จัดการพร้อมกันได้ การเข้าถึงบัฟเฟอร์พร้อมกันเพื่อเขียนหรืออ่าน/เขียน ไม่ได้รับการกำหนด แต่ต้องไม่ทำให้บริการไดรเวอร์ขัดข้องหรือบล็อกผู้เรียก อย่างไม่มีกำหนด ไดรเวอร์อาจแสดงข้อผิดพลาดหรือปล่อยให้เนื้อหาของบัฟเฟอร์อยู่ในสถานะ ที่ไม่แน่นอน