حوضچه های حافظه

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

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

  • اگر طول عمر CONSTANT_COPY باشد، مقادیر در فیلد operandValues ​​از ساختار مدل قرار می‌گیرند. از آنجا که مقادیر موجود در بردار HIDL در طول ارتباط بین پردازشی (IPC) کپی می‌شوند، این معمولاً فقط برای نگهداری مقدار کمی از داده‌ها مانند عملوندهای اسکالر (به عنوان مثال، اسکالر فعال‌سازی در ADD ) و پارامترهای تانسور کوچک (به عنوان مثال، تانسور شکل در RESHAPE ) استفاده می‌شود.
  • اگر طول عمر CONSTANT_REFERENCE باشد، مقادیر در فیلد pools ساختار مدل قرار می‌گیرند. در طول IPC فقط هندل‌های poolهای حافظه مشترک کپی می‌شوند و نیازی به کپی کردن مقادیر خام نیست. بنابراین، نگهداری حجم زیادی از داده‌ها (مثلاً پارامترهای وزنی در کانولوشن‌ها) با استفاده از poolهای حافظه مشترک نسبت به بردارهای 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 نوعی حافظه مشترک است که یک بافر Gralloc را در بر می‌گیرد. در اندروید ۱۰، API شبکه‌های عصبی (NNAPI) از استفاده از AHardwareBuffer پشتیبانی می‌کند و به درایور اجازه می‌دهد بدون کپی کردن داده‌ها، اجراها را انجام دهد که این امر عملکرد و مصرف برق برنامه‌ها را بهبود می‌بخشد. به عنوان مثال، یک پشته HAL دوربین می‌تواند اشیاء AHardwareBuffer را برای بارهای کاری یادگیری ماشینی با استفاده از دسته‌های AHardwareBuffer تولید شده توسط APIهای 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 buffer را کدگذاری می‌کند.

درایور باید فیلد hidl_handle ارائه شده را به درستی رمزگشایی کرده و به حافظه‌ای که توسط hidl_handle توصیف شده است دسترسی پیدا کند. هنگامی که متدهای getSupportedOperations_1_2 ، getSupportedOperations_1_1 یا getSupportedOperations فراخوانی می‌شوند، درایور باید تشخیص دهد که آیا می‌تواند hidl_handle ارائه شده را رمزگشایی کرده و به حافظه‌ای که توسط hidl_handle توصیف شده است دسترسی پیدا کند یا خیر. اگر فیلد hidl_handle مورد استفاده برای یک عملوند ثابت پشتیبانی نشود، آماده‌سازی مدل باید با شکست مواجه شود. اگر فیلد hidl_handle مورد استفاده برای یک عملوند ورودی یا خروجی اجرا پشتیبانی نشود، اجرا باید با شکست مواجه شود. توصیه می‌شود درایور در صورت عدم موفقیت آماده‌سازی یا اجرای مدل، کد خطای GENERAL_FAILURE را برگرداند.

دامنه‌های حافظه

برای دستگاه‌هایی که اندروید ۱۱ یا بالاتر را اجرا می‌کنند، NNAPI از دامنه‌های حافظه‌ای پشتیبانی می‌کند که رابط‌های تخصیص‌دهنده برای بافرهای مدیریت‌شده توسط درایور را فراهم می‌کنند. این امر امکان انتقال حافظه‌های بومی دستگاه را در طول اجراها فراهم می‌کند و از کپی کردن و تبدیل غیرضروری داده‌ها بین اجراهای متوالی در همان درایور جلوگیری می‌کند. این جریان در شکل ۱ نشان داده شده است.

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

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

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

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

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

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

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

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

  • مقداردهی اولیه تانسور حالت
  • ذخیره نتایج میانی
  • اجرای Fallback روی CPU

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

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

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

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

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