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

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

ณ เวลาการคอมไพล์โมเดล กรอบงานจะจัดเตรียมค่าของตัวถูกดำเนินการคงที่ให้กับไดรเวอร์ ขึ้นอยู่กับอายุการใช้งานของตัวถูกดำเนินการคงที่ ค่าของมันจะอยู่ในเวกเตอร์ HIDL หรือพูลหน่วยความจำแบบแบ่งใช้

  • หากอายุการใช้งานคือ CONSTANT_COPY ค่าจะอยู่ในฟิลด์ operandValues ​​ของโครงสร้างโมเดล เนื่องจากค่าในเวกเตอร์ HIDL ถูกคัดลอกระหว่างการสื่อสารระหว่างกระบวนการ (IPC) โดยทั่วไปจะใช้เพื่อเก็บข้อมูลจำนวนเล็กน้อยเท่านั้น เช่น ตัวถูกดำเนินการสเกลาร์ (เช่น สเกลาร์การเปิดใช้งานใน ADD ) และพารามิเตอร์เทนเซอร์ขนาดเล็ก (เช่น เทนเซอร์รูปร่างใน RESHAPE )
  • หากอายุการใช้งานคือ CONSTANT_REFERENCE ค่าจะอยู่ในฟิลด์ pools ของโครงสร้างโมเดล เฉพาะหมายเลขอ้างอิงของพูลหน่วยความจำที่ใช้ร่วมกันเท่านั้นที่จะถูกทำซ้ำระหว่าง IPC แทนที่จะคัดลอกค่าดิบ ดังนั้นจึงมีประสิทธิภาพมากกว่าในการเก็บข้อมูลจำนวนมาก (เช่น พารามิเตอร์น้ำหนักในรูปแบบ Convolution) โดยใช้พูลหน่วยความจำแบบแบ่งใช้มากกว่าเวกเตอร์ HIDL

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

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

  • ashmem : หน่วยความจำที่แชร์ของ Android สำหรับรายละเอียดเพิ่มเติม โปรดดูที่ หน่วยความจำ
  • mmap_fd : หน่วยความจำที่ใช้ร่วมกันได้รับการสนับสนุนโดยตัวอธิบายไฟล์ผ่าน mmap
  • hardware_buffer_blob : หน่วยความจำที่ใช้ร่วมกันซึ่งได้รับการสนับสนุนโดย AHardwareBuffer ด้วยรูปแบบ AHARDWARE_BUFFER_FORMAT_BLOB พร้อมใช้งานจากโครงข่ายประสาทเทียม (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 ด้วย การสนับสนุนสำหรับ hardware_buffer และโดเมนหน่วยความจำโหมดที่ไม่ใช่ BLOB ทั่วไปเป็นทางเลือก

Aฮาร์ดแวร์บัฟเฟอร์

AHardwareBuffer เป็นหน่วยความจำที่ใช้ร่วมกันประเภทหนึ่งที่ล้อม รอบบัฟเฟอร์ Gralloc ใน Android 10 นั้น Neural Networks API (NNAPI) รองรับการใช้ AHardwareBuffer ทำให้ไดรเวอร์ดำเนินการได้โดยไม่ต้องคัดลอกข้อมูล ซึ่งปรับปรุงประสิทธิภาพและการใช้พลังงานสำหรับแอป ตัวอย่างเช่น สแต็ก HAL ของกล้องสามารถส่งผ่านออบเจ็กต์ AHardwareBuffer ไปยัง NNAPI สำหรับปริมาณงานการเรียนรู้ของเครื่องโดยใช้ตัวจัดการ AHardwareBuffer ที่สร้างโดยกล้อง NDK และ NDK API ของสื่อ สำหรับข้อมูลเพิ่มเติม โปรดดูที่ ANeuralNetworksMemory_createFromAHardwareBuffer

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

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

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

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