این صفحه ساختارهای داده و روشهای مورد استفاده برای برقراری ارتباط موثر بافرهای عملوند بین درایور و چارچوب را توضیح میدهد.
در زمان کامپایل مدل، چارچوب مقادیر عملوندهای ثابت را در اختیار راننده قرار می دهد. بسته به طول عمر عملوند ثابت، مقادیر آن یا در یک بردار HIDL یا یک مخزن حافظه مشترک قرار دارند.
- اگر طول عمر
CONSTANT_COPY
باشد، مقادیر در قسمتoperandValues
ساختار مدل قرار دارند. از آنجایی که مقادیر موجود در بردار HIDL در طول ارتباطات بین فرآیندی (IPC) کپی میشوند، این معمولاً فقط برای نگهداری مقدار کمی از دادهها مانند عملوندهای اسکالر (مثلاً اسکالر فعالسازی درADD
) و پارامترهای تانسور کوچک (مثلاً تانسور شکل درRESHAPE
). - اگر طول عمر
CONSTANT_REFERENCE
باشد، مقادیر در قسمتpools
ساختار مدل قرار می گیرند. فقط دسته های استخرهای حافظه مشترک در طول IPC به جای کپی کردن مقادیر خام، کپی می شوند. بنابراین، نگهداری حجم زیادی از داده ها (مثلاً پارامترهای وزن در کانولوشن ها) با استفاده از استخرهای حافظه مشترک نسبت به بردارهای HIDL کارآمدتر است.
در زمان اجرای مدل، چارچوب بافرهای عملوندهای ورودی و خروجی را در اختیار راننده قرار می دهد. برخلاف ثابتهای زمان کامپایل که ممکن است در یک بردار HIDL ارسال شوند، دادههای ورودی و خروجی یک اجرا همیشه از طریق مجموعهای از استخرهای حافظه ارتباط برقرار میکنند.
نوع داده HIDL hidl_memory
هم در کامپایل و هم در اجرا برای نشان دادن یک مخزن حافظه مشترک نگاشت نشده استفاده می شود. درایور باید حافظه را بر اساس آن نقشه برداری کند تا بر اساس نام نوع داده hidl_memory
قابل استفاده باشد. نام حافظه های پشتیبانی شده عبارتند از:
-
ashmem
: حافظه مشترک اندروید. برای جزئیات بیشتر، حافظه را ببینید. -
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
نیز پشتیبانی کنند. پشتیبانی از حالت عمومی غیر BLOB hardware_buffer
و دامنه های حافظه اختیاری است.
AHardwareBuffer
AHardwareBuffer نوعی حافظه مشترک است که یک بافر Gralloc را می پوشاند. در اندروید 10، API شبکههای عصبی (NNAPI) از استفاده از AHardwareBuffer پشتیبانی میکند و به درایور اجازه میدهد تا بدون کپی کردن دادهها، اجراها را انجام دهد که عملکرد و مصرف انرژی برنامهها را بهبود میبخشد. برای مثال، یک پشته HAL دوربین میتواند اشیاء AHardwareBuffer را برای بارهای کاری یادگیری ماشین با استفاده از دستههای AHardwareBuffer تولید شده توسط دوربین NDK و APIهای رسانه NDK به NNAPI ارسال کند. برای اطلاعات بیشتر، به ANeuralNetworksMemory_createFromAHardwareBuffer
مراجعه کنید.
اشیاء AHardwareBuffer مورد استفاده در NNAPI از طریق ساختار hidl_memory
به نام hardware_buffer
یا hardware_buffer_blob
به درایور منتقل میشوند. ساختار hidl_memory
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
پیاده سازی کند. برای درایور اختیاری است که از hardware_buffer
حالت غیر BLOB پشتیبانی کند.
در طول تخصیص بافر، ابعاد بافر را می توان از عملوندهای مدل متناظر تمام نقش های مشخص شده توسط BufferRole
و ابعاد ارائه شده در BufferDesc
استنتاج کرد. با ترکیب تمام اطلاعات ابعادی، بافر ممکن است ابعاد یا رتبه ناشناخته ای داشته باشد. در چنین حالتی، بافر در یک حالت انعطاف پذیر است که در آن ابعاد زمانی که به عنوان ورودی مدل استفاده می شود و در حالت پویا زمانی که به عنوان خروجی مدل استفاده می شود، ثابت است. بافر یکسان را می توان با اشکال مختلف خروجی در اجراهای مختلف استفاده کرد و راننده باید تغییر اندازه بافر را به درستی مدیریت کند.
دامنه حافظه یک ویژگی اختیاری است. یک راننده می تواند تشخیص دهد که به دلایلی نمی تواند از یک درخواست تخصیص خاص پشتیبانی کند. به عنوان مثال:
- بافر درخواستی دارای اندازه پویا است.
- درایور دارای محدودیتهای حافظه است که از مدیریت بافرهای بزرگ جلوگیری میکند.
این امکان وجود دارد که چندین رشته مختلف به طور همزمان از بافر مدیریت شده توسط درایور خوانده شوند. دسترسی همزمان به بافر برای نوشتن یا خواندن/نوشتن تعریف نشده است، اما نباید سرویس راننده را از کار بیندازد یا تماس گیرنده را به طور نامحدود مسدود کند. درایور می تواند یک خطا را برگرداند یا محتوای بافر را در حالت نامشخص قرار دهد.