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

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

در زمان کامپایل مدل، چارچوب مقادیر عملوندهای ثابت را در اختیار راننده قرار می دهد. بسته به طول عمر عملوند ثابت، مقادیر آن یا در یک بردار 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 استنتاج کرد. با ترکیب تمام اطلاعات ابعادی، بافر ممکن است ابعاد یا رتبه ناشناخته ای داشته باشد. در چنین حالتی، بافر در یک حالت انعطاف پذیر است که در آن ابعاد زمانی که به عنوان ورودی مدل استفاده می شود و در حالت پویا زمانی که به عنوان خروجی مدل استفاده می شود، ثابت است. بافر یکسان را می توان با اشکال مختلف خروجی در اجراهای مختلف استفاده کرد و راننده باید تغییر اندازه بافر را به درستی مدیریت کند.

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

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

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

،

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

در زمان کامپایل مدل، چارچوب مقادیر عملوندهای ثابت را در اختیار راننده قرار می دهد. بسته به طول عمر عملوند ثابت، مقادیر آن یا در یک بردار 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 استنتاج کرد. با ترکیب تمام اطلاعات ابعادی، بافر ممکن است ابعاد یا رتبه ناشناخته ای داشته باشد. در چنین حالتی، بافر در یک حالت انعطاف پذیر است که در آن ابعاد زمانی که به عنوان ورودی مدل استفاده می شود و در حالت پویا زمانی که به عنوان خروجی مدل استفاده می شود، ثابت است. بافر یکسان را می توان با اشکال مختلف خروجی در اجراهای مختلف استفاده کرد و راننده باید تغییر اندازه بافر را به درستی مدیریت کند.

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

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

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