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

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

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

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

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

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

  • ashmem: หน่วยความจำที่ใช้ร่วมกันของ Android ดูรายละเอียดเพิ่มเติมได้ที่หน่วยความจำ
  • mmap_fd: หน่วยความจำที่ใช้ร่วมกันซึ่งมาจากข้อบ่งชี้ไฟล์ถึง mmap
  • hardware_buffer_blob: หน่วยความจำที่ใช้ร่วมกันซึ่งได้รับการสนับสนุนโดย A hardwareBuffer โดยใช้รูปแบบ AHARDWARE_BUFFER_FORMAT_BLOB พร้อมให้บริการผ่าน Neural Networks (NN) HAL 1.2 ดูรายละเอียดเพิ่มเติมได้ที่ A hardwareBuffer
  • hardware_buffer: หน่วยความจำที่ใช้ร่วมกันซึ่งมาจาก Aฮาร์ดแวร์Buffer ทั่วไปที่ไม่ได้ใช้รูปแบบ AHARDWARE_BUFFER_FORMAT_BLOB บัฟเฟอร์ฮาร์ดแวร์ในโหมดที่ไม่ใช่ BLOB ใช้ได้ในการดำเนินการโมเดลเท่านั้น ซึ่งมีให้ที่ NNHAL 1.2 ดูรายละเอียดเพิ่มเติมได้ที่ A hardwareBuffer

ตั้งแต่ NN HAL 1.3 เป็นต้นไป NNAPI รองรับโดเมนหน่วยความจำซึ่งมีอินเทอร์เฟซที่จัดสรรสำหรับบัฟเฟอร์ที่จัดการโดยไดรเวอร์ นอกจากนี้บัฟเฟอร์ที่จัดการโดยไดรเวอร์ยังใช้เป็นอินพุตหรือเอาต์พุตของการดำเนินการได้ด้วย โปรดดูรายละเอียดเพิ่มเติมที่หัวข้อโดเมนหน่วยความจำ

ไดรเวอร์ NNAPI ต้องรองรับการแมปชื่อหน่วยความจำ ashmem และ mmap_fd จาก NN HAL 1.3 ไดรเวอร์ต้องรองรับการแมปของ hardware_buffer_blob ด้วย การสนับสนุนสำหรับโหมด hardware_buffer ทั่วไปที่ไม่ใช่ BLOB และโดเมนหน่วยความจำเป็นตัวเลือกที่ไม่บังคับ

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

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

ออบเจ็กต์ Aฮาร์ดแวร์Buffer ที่ใช้ใน NNAPI จะส่งไปยังไดรเวอร์ผ่านโครงสร้าง hidl_memory ที่ชื่อ hardware_buffer หรือ hardware_buffer_blob โครงสร้าง hidl_memory hardware_buffer_blob แสดงเฉพาะออบเจ็กต์ Aฮาร์ดแวร์Buffer ที่มีรูปแบบ AHARDWAREBUFFER_FORMAT_BLOB

ข้อมูลที่เฟรมเวิร์กต้องการได้รับการเข้ารหัสในช่อง hidl_handle ของโครงสร้าง hidl_memory ช่อง hidl_handle จะรวม native_handle ซึ่งเข้ารหัสข้อมูลเมตาที่จำเป็นทั้งหมดเกี่ยวกับ Aฮาร์ดแวร์Buffer หรือบัฟเฟอร์ 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 บัฟเฟอร์โฟลว์ข้อมูลโดยใช้โดเมนหน่วยความจำ

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

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

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

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

โทเค็นจาก IDevice::allocate มีการระบุเมื่ออ้างอิงบัฟเฟอร์ในฐานะหนึ่งในออบเจ็กต์ MemoryPool ในโครงสร้าง Request ของการดำเนินการ หากต้องการป้องกันไม่ให้กระบวนการพยายามเข้าถึงบัฟเฟอร์ที่จัดสรรไว้ในกระบวนการอื่น ไดรเวอร์ต้องใช้การตรวจสอบที่เหมาะสมทุกครั้งที่ใช้บัฟเฟอร์ ไดรเวอร์ต้องตรวจสอบว่าการใช้บัฟเฟอร์เป็นหนึ่งในบทบาท BufferRole ที่ระบุในระหว่างการจัดสรร และต้องดำเนินการไม่สำเร็จในทันทีหากการใช้งานนั้นผิดกฎหมาย

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

  • การเริ่มต้น Tensor สถานะ
  • การแคชผลลัพธ์ระดับกลาง
  • การดำเนินการสำรองบน CPU

ไดรเวอร์ต้องใช้ IBuffer::copyTo และ IBuffer::copyFrom กับ ashmem, mmap_fd และ hardware_buffer_blob หากรองรับการจัดสรรโดเมนหน่วยความจำเพื่อรองรับกรณีการใช้งานเหล่านี้ พนักงานขับรถจะรองรับโหมดที่ไม่ใช่ BLOB หรือไม่ก็ได้ hardware_buffer

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

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

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

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