استخرهای حافظه

این صفحه ساختارهای داده و روش‌های استفاده‌شده برای انتقال کارآمد بافرهای عملوند بین درایور و چارچوب را شرح می‌دهد.

در زمان کامپایل مدل، چارچوب مقادیر عملوندهای ثابت را به درایور ارائه می‌دهد. بسته به طول عمر عملوند ثابت، مقادیر آن یا در بردار HIDL یا در استخر حافظه مشترک قرار می‌گیرد.

  • اگر طول عمر CONSTANT_COPY باشد، مقادیر در فیلد operandValues ساختار مدل قرار می‌گیرند. ازآنجایی‌که مقادیر در بردار HIDL درطول ارتباط بین‌پردازشی (IPC) کپی می‌شوند، این بردار معمولاً فقط برای نگهداری مقدار کمی از داده‌ها مثل عملوندهای اسکالر (برای نمونه، اسکالر فعال‌سازی در ADD) و پارامترهای تانسور کوچک (برای نمونه، تانسور شکل در RESHAPE) استفاده می‌شود.
  • اگر طول عمر CONSTANT_REFERENCE باشد، مقادیر در فیلد pools ساختار مدل قرار می‌گیرند. درطول «ارتباط بین‌پردازشی»، فقط دسته‌های استخرهای حافظه مشترک تکثیر می‌شوند و مقادیر خام کپی نمی‌شوند. بنابراین، نگهداری حجم زیادی از داده‌ها (برای مثال، پارامترهای وزن در پیچیدگی‌ها) بااستفاده از استخرهای حافظه مشترک نسبت‌به بردار 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، «میانای برنامه‌سازی کاربردی شبکه‌های عصبی» (NNAPI) از استفاده از AHardwareBuffer پشتیبانی می‌کند، که به درایور امکان می‌دهد اجراها را بدون کپی کردن داده‌ها انجام دهد، که باعث بهبود عملکرد و مصرف انرژی برنامه‌ها می‌شود. برای مثال، پشته HAL دوربین می‌تواند اشیای AHardwareBuffer را برای بارهای کاری یادگیری ماشین بااستفاده از دسته‌های AHardwareBuffer تولیدشده توسط واسط‌های برنامه‌نویسی NDK دوربین و 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 از دامنه‌های حافظه که رابط‌های تخصیص‌دهنده برای بافرهای مدیریت‌شده توسط درایور ارائه می‌دهند پشتیبانی می‌کند. این کار امکان می‌دهد حافظه‌های بومی دستگاه در سراسر اجراها منتقل شود و کپی و تبدیل داده‌های غیرضروری بین اجراهای متوالی در یک درایور سرکوب شود. این جریان در شکل ۱ نشان داده شده است.

جریان داده بافر با و بدون دامنه‌های حافظه

شکل ۱. میان‌گیر کردن جریان داده بااستفاده از دامنه‌های حافظه

ویژگی دامنه حافظه برای تانسورهایی درنظر گرفته شده است که عمدتاً داخلی درایور هستند و نیازی به دسترسی مکرر در سمت کارخواه ندارند. نمونه‌هایی از چنین تانسورهایی شامل تانسورهای حالت در مدل‌های توالی می‌شود. برای تانسورهایی که نیاز به دسترسی مکرر به واحد پردازش مرکزی در سمت مشتری دارند، بهتر است از استخرهای حافظه مشترک استفاده شود.

برای پشتیبانی از ویژگی دامنه حافظه، IDevice::allocate را پیاده‌سازی کنید تا چارچوب بتواند تخصیص بافر مدیریت‌شده توسط درایور را درخواست کند. درطول تخصیص، چارچوب ویژگی‌ها و الگوهای استفاده زیر را برای بافر ارائه می‌دهد:

  • BufferDesc خصوصیات موردنیاز بافر را توصیف می‌کند.
  • BufferRole الگوی استفاده بالقوه از بافر را به‌عنوان ورودی یا خروجی مدل آماده‌شده توصیف می‌کند. درطول تخصیص ظرفیت ذخیره، می‌توان نقش‌های متعددی را مشخص کرد و از ظرفیت ذخیره تخصیص‌یافته فقط می‌توان به‌عنوان نقش‌های مشخص‌شده استفاده کرد.

میان‌گیری تخصیص‌یافته درایور داخلی است. گرداننده می‌تواند هر چیدمان داده یا مکان میان‌گیری را انتخاب کند. وقتی بافر باموفقیت تخصیص داده می‌شود، کاربر درایور می‌تواند بااستفاده از نماد برگشتی یا شیء IBuffer به بافر ارجاع دهد یا با آن تعامل داشته باشد.

وقتی به میان‌گیر به‌عنوان یکی از اشیای MemoryPool در ساختار Request اجرا ارجاع داده می‌شود، توکن از IDevice::allocate ارائه می‌شود. برای جلوگیری از تلاش یک فرایند برای دسترسی به میان‌گیر اختصاص‌داده‌شده در فرایند دیگر، درایور باید هنگام هر بار استفاده از میان‌گیر، اعتبارسنجی مناسب را اعمال کند. درایور باید تأیید کند که استفاده از میان‌گیر یکی از BufferRole نقش‌های ارائه‌شده درطول تخصیص است و اگر استفاده غیرقانونی باشد، باید فوراً اجرا را متوقف کند.

از IBuffer شیء برای کپی کردن حافظه صریح استفاده می‌شود. در شرایط خاص، مشتری درایور باید بافر مدیریت‌شده توسط درایور را از استخر حافظه مشترک مقداردهی اولیه کند یا بافر را در استخر حافظه مشترک کپی کند. موارد استفاده نمونه عبارت‌اند از:

  • مقداردهی اولیه تانسور حالت
  • ذخیره نتایج واسط در حافظه نهان
  • اجرای جایگزین در واحد پردازش مرکزی

برای پشتیبانی از این موارد استفاده، درایور باید IBuffer::copyTo و IBuffer::copyFrom را با ashmem، mmap_fd، و hardware_buffer_blob پیاده‌سازی کند، اگر از تخصیص دامنه حافظه پشتیبانی می‌کند. پشتیبانی از حالت غیر BLOB برای درایور اختیاری است hardware_buffer.

درطول تخصیص بافر، ابعاد بافر را می‌توان از عملوندهای مدل مربوط به همه نقش‌های مشخص‌شده توسط BufferRole و ابعاد ارائه‌شده در BufferDesc استنباط کرد. با ترکیب همه اطلاعات ابعادی، میان‌گیر ممکن است ابعاد یا رتبه نامشخصی داشته باشد. در چنین شرایطی، میان‌گیر در حالت انعطاف‌پذیر قرار دارد که در آن ابعاد هنگام استفاده به‌عنوان ورودی مدل ثابت است و هنگام استفاده به‌عنوان خروجی مدل در حالت پویا قرار دارد. از همان میان‌گیر می‌توان با شکل‌های مختلف برونداد در اجراهای مختلف استفاده کرد و درایور باید تغییر اندازه میان‌گیر را به‌درستی مدیریت کند.

دامنه حافظه ویژگی اختیاری است. به‌دلایل مختلف، درایور می‌تواند تشخیص دهد که نمی‌تواند از درخواست تخصیص داده‌شده‌ای پشتیبانی کند. برای مثال:

  • میان‌گیری درخواستی اندازه پویا دارد.
  • درایور محدودیت‌های حافظه دارد که مانع از مدیریت میان‌گیرهای بزرگ می‌شود.

چندین رشته مختلف می‌توانند به‌طور هم‌زمان از بافر مدیریت‌شده توسط درایور بخوانند. دسترسی هم‌زمان به میان‌گیر برای نوشتن یا خواندن/نوشتن تعریف نشده است، اما نباید باعث ازکار افتادن سرویس درایور شود یا تماس‌گیرنده را به‌طور نامحدود مسدود کند. گرداننده می‌تواند خطا برگرداند یا محتوای میان‌گیر را در وضعیت نامشخصی قرار دهد.