مجموعات الذاكرة

توضِّح هذه الصفحة هياكل البيانات والأساليب المستخدَمة في التواصل بفعالية مع مخازن الوسائط بين برنامج التشغيل والإطار.

في وقت تجميع النموذج، يقدّم الإطار القيم للمعاملات الثابتة إلى برنامج التشغيل. اعتمادًا على مدة بقاء المعامل الثابت، توجد قيمه إما في متجه HIDL أو في مجموعة ذاكرة مشتركة.

  • إذا كانت الفترة الزمنية هي CONSTANT_COPY، تكون القيم متوفّرة في حقل operandValues في بنية النموذج. بما أنّه يتم نسخ القيم في ملف ملف تعريف HIDL أثناء عملية التواصل بين العمليات (IPC)، لا يتم استخدام هذا الملف عادةً إلا لتخزين كمية صغيرة من البيانات، مثل المعاملات السلاسل المتغيرة (على سبيل المثال، المعامل السلاسل المتغيرة للتنشيط في ADD) ومَعلمات مصفوفات صغيرة (على سبيل المثال، مصفوفة الشكل في RESHAPE).
  • إذا كانت الفترة الزمنية هي CONSTANT_REFERENCE، تكون القيم متوفّرة في حقل pools في بنية النموذج. يتم تكرار مقابض مجموعات الذاكرة المشتركة فقط أثناء IPC بدلاً من نسخ القيم الأولية. لذلك، من الأفضل الاحتفاظ بكمية كبيرة من البيانات (مثل مَعلمات الوزن في عمليات التفاف البيانات) باستخدام مجموعات الذاكرة المشتركة بدلاً من 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. لمعرفة مزيد من التفاصيل، يُرجى الاطّلاع على ADEVICEBuffer.
  • hardware_buffer: ذاكرة مشترَكة مستندة إلى AHardwareBuffer عام لا يستخدم التنسيق AHARDWARE_BUFFER_FORMAT_BLOB لا يتوفّر وضع عدم استخدام BLOB التخزين المؤقت للأجهزة إلا في تنفيذ النماذج.يتوفّر من NN HAL 1.2. لمزيد من التفاصيل، يُرجى الاطّلاع على AHardwareBuffer.

اعتبارًا من الإصدار 1.3 من NN HAL، تتيح NNAPI نطاقات الذاكرة التي توفّر واجهات تخصيص للمخازن المؤقتة التي يديرها برنامج التشغيل. يمكن أيضًا استخدام ملفّات التخزين المؤقت التي يديرها برنامج التشغيل كمدخلات أو مخرجات للتنفيذ. لمزيد من التفاصيل، يُرجى الاطّلاع على نطاقات الذاكرة.

يجب أن تتيح برامج تشغيل NNAPI إمكانية ربط أسماء الذاكرة ashmem وmmap_fd. اعتبارًا من NN HAL 1.3، يجب أن تتيح برامج التشغيل أيضًا ربط hardware_buffer_blob. إنّ توفير دعم للوضع العام غير المخصّص لتنسيق BLOB hardware_buffer ونطاقات الذاكرة هو أمر اختياري.

AHardwareBuffer

AHardwareBuffer هو نوع من الذاكرة المشتركة التي تُغلِّف عمود Gralloc. في الإصدار 10 من Android، تتيح واجهة برمجة التطبيقات Neural Networks API (NNAPI) استخدام AHardwareBuffer، مما يسمح لبرنامج التشغيل بتنفيذ عمليات بدون نسخ البيانات، ما يؤدي إلى تحسين الأداء واستهلاك الطاقة للتطبيقات. على سبيل المثال، يمكن لحزمة HAL الكاميرا تمرير عناصر AHardwareBuffer إلى NNAPI لأعمال تعلُّم الآلة باستخدام عناصر تحكّم AHardwareBuffer التي تم إنشاؤها من خلال واجهات برمجة التطبيقات NDK للكاميرا وNDK للوسائط. لمزيد من المعلومات، يُرجى الاطّلاع على 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: تخزين تدفق البيانات باستخدام نطاقات الذاكرة

تم تصميم ميزة مجال الذاكرة لأجهزة التوتر التي تكون غالبًا داخلية في برنامج التشغيل ولا تحتاج إلى وصول متكرر من جانب العميل. وتشمل أمثلة ملفّات الملفات هذه ملفات الحالة في نماذج التسلسل. بالنسبة إلى مصفوفات Tensor التي تحتاج إلى الوصول المتكرّر إلى وحدة المعالجة المركزية من جهة العميل، من الأفضل استخدام مجموعات الذاكرة المشتركة.

لتفعيل ميزة نطاق الذاكرة، نفِّذ IDevice::allocate للسماح للإطار بطلب تخصيص المخزن المؤقت الذي يديره برنامج التشغيل. أثناء التخصيص، يقدّم إطار العمل السمات التالية وأنماط الاستخدام للذاكرة المؤقتة:

  • BufferDesc يصف السمات المطلوبة للوسيط.
  • يصف BufferRole نمط الاستخدام المحتمل للذاكرة المؤقتة كمدخل أو مخرج لنموذج معدّ. يمكن تحديد أدوار متعددة أثناء تخصيص المخزن المؤقت، ويمكن استخدام المخزن المؤقت المخصص فقط كأدوار محددة.

ويكون المخزن المؤقت المخصّص داخليًا للسائق. يمكن لبرنامج تشغيل اختيار أي موقع ملف مؤقت أو تنسيق بيانات. عند تخصيص المخزن المؤقت بنجاح، يمكن لعميل برنامج التشغيل الإشارة إلى المخزن المؤقت أو التفاعل معه باستخدام الرمز المميّز الذي تم إرجاعه أو عنصر IBuffer.

يتم تقديم الرمز المميّز من IDevice::allocate عند الإشارة إلى المخزن المؤقت على أنّه أحد عناصر MemoryPool في بنية تنفيذ Request. لمنع أي عملية من محاولة الوصول إلى المخزن المؤقت المخصّص في عملية أخرى، يجب أن يطبّق برنامج التشغيل عملية التحقّق المناسبة عند كل استخدام للمخزن المؤقت. على برنامج التشغيل التحقّق من أنّ استخدام المخزن المؤقت هو أحد أدوار BufferRole المقدَّمة أثناء التخصيص، ويجب أن يوقف التنفيذ على الفور إذا كان الاستخدام غير قانوني.

يتم استخدام كائن IBuffer لنسخ الذاكرة الصريح. في حالات معيّنة، يجب أن يُنشئ العميل للبرنامج الوسيط ذاكرة تخزين مؤقتة تديرها وحدة التحكّم من مجموعة ذاكرة مشترَكة أو ينسخ الذاكرة التخزينية المؤقتة إلى مجموعة ذاكرة مشترَكة. تشمل أمثلة حالات الاستخدام ما يلي:

  • إعداد مصفوفة الحالة
  • التخزين المؤقت للنتائج المتوسطة
  • التنفيذ الاحتياطي على وحدة المعالجة المركزية

لدعم حالات الاستخدام هذه، يجب أن ينفِّذ برنامج التشغيل IBuffer::copyTo و IBuffer::copyFrom مع ashmem وmmap_fd وhardware_buffer_blob إذا كان يتوافق مع منح مجال الذاكرة. من الخيارات المتاحة لبرنامج التشغيل أن يتيح وضع عدم استخدام BLOB hardware_buffer.

أثناء تخصيص المخزن المؤقت، يمكن استنتاج سمات المخزن المؤقت من عوامل التشغيل النموذجية المقابلة لجميع الأدوار المحدّدة بواسطة BufferRole، و السمات المقدّمة في BufferDesc. مع تجميع كل معلومات الأبعاد معًا، قد يكون للمخزن المؤقت أبعاد أو ترتيب غير معروف. وفي هذه الحالة، يكون المورد الاحتياطي في حالة مرنة حيث تكون الأبعاد ثابتة عند استخدامها كإدخال نموذج وفي حالة ديناميكية عند استخدامه كمخرجات نموذج. يمكن استخدام ملف التخزين المؤقت نفسه بأشكال مختلفة من النتائج في عمليات التنفيذ المختلفة، ويجب أن يتعامل برنامج التشغيل مع إعادة ضبط حجم ملف التخزين المؤقت بشكل صحيح.

نطاق الذاكرة ميزة اختيارية. يمكن للسائق تحديد أنّه لا يمكنه تلبية طلب تخصيص معيّن لعدد من الأسباب. مثلاً:

  • ذاكرة التخزين المؤقت المطلوبة لها حجم ديناميكي.
  • يواجه برنامج تشغيل الجهاز قيودًا في الذاكرة تمنع استخدامه لمخازن مؤقتة كبيرة.

من الممكن أن تقرأ عدة سلاسل محادثات مختلفة من ملف التخزين المؤقت الذي يديره برنامج التشغيل بشكل متزامن. لا يمكن تحديد ما إذا كان الوصول إلى المخزن المؤقت في وقت واحد للكتابة أو القراءة/الكتابة يؤدي إلى حدوث خطأ، ولكن يجب ألّا يؤدي ذلك إلى تعطُّل خدمة برنامج التشغيل أو حظر المُتصل بشكلٍ دائم. يمكن أن يعرض برنامج التشغيل خطأ أو يترك محتوى المخزن المؤقت في حالة غير محددة.