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

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

في وقت تجميع النموذج، يوفر إطار العمل قيم المعاملات الثابتة لبرنامج التشغيل. واعتمادًا على عمر المعامل الثابت، توجد قيمه إما في متجه 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: الذاكرة المشتركة مدعومة بواسطة ADEVICEBuffer بالتنسيق AHARDWARE_BUFFER_FORMAT_BLOB. متوفر من الشبكات العصبية (NN) HAL 1.2. لمعرفة مزيد من التفاصيل، يُرجى الاطّلاع على ADEVICEBuffer.
  • hardware_buffer: الذاكرة المشتركة مدعومة بواسطة برنامج ADEVICEBuffer عام لا يستخدم التنسيق AHARDWARE_BUFFER_FORMAT_BLOB. لا يتم دعم المخزن المؤقت للأجهزة في وضع غير تخزين البيانات الثنائية الكبيرة إلا في تنفيذ النموذج.ويتوفر من NN HAL 1.2. لمعرفة مزيد من التفاصيل، يُرجى الاطّلاع على ADEVICEBuffer.

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

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

مخزن الأجهزة

ADeviceBuffer هو نوع من الذاكرة المشتركة يلتف مخزن Gralloc المؤقت. في نظام التشغيل Android 10، تدعم واجهة برمجة التطبيقات العصبية (NNAPI) استخدام ADEVICEBuffer، ما يتيح للسائق تنفيذ عمليات التنفيذ بدون نسخ البيانات، ما يؤدي إلى تحسين أداء التطبيقات واستهلاك الطاقة لها. على سبيل المثال، يمكن لحزمة HAL للكاميرا تمرير كائنات ADeviceBuffer إلى NNAPI لأحمال عمل التعلم الآلي باستخدام مقابض A ApplianceBuffer التي تم إنشاؤها بواسطة الكاميرا NDK وواجهة برمجة تطبيقات Media NDK. لمزيد من المعلومات، يُرجى الاطّلاع على ANeuralNetworksMemory_createFromAHardwareBuffer.

يتم تمرير كائنات A ApplianceBuffer المستخدمة في NNAPI إلى برنامج التشغيل من خلال هيكل hidl_memory باسم hardware_buffer أو hardware_buffer_blob. تمثّل بنية hardware_buffer_blob hidl_memory فقط كائنات ADEVICEBuffer ذات التنسيق AHARDWAREBUFFER_FORMAT_BLOB.

يتم ترميز المعلومات التي يطلبها إطار العمل في الحقل hidl_handle من بنية hidl_memory. ويلتف الحقل hidl_handle الحقل native_handle، الذي يرمّز جميع البيانات الوصفية المطلوبة حول ADeviceBuffer أو Gralloc مؤقتًا.

يجب أن يفك برنامج التشغيل ترميز حقل hidl_handle المقدَّم بشكل صحيح وأن يصل إلى الذاكرة الموضَّحة في hidl_handle. عند استدعاء طريقة getSupportedOperations_1_2 أو getSupportedOperations_1_1 أو getSupportedOperations، يجب أن يكتشف برنامج التشغيل ما إذا كان بإمكانه فك ترميز hidl_handle المقدَّم والوصول إلى الذاكرة الموضّحة في hidl_handle. يجب أن يتعذّر تحضير النموذج إذا لم يكن الحقل hidl_handle المستخدَم للمعامل الثابت متاحًا. يجب أن تفشل عملية التنفيذ إذا لم يكن الحقل hidl_handle المستخدَم في معامل الإدخال أو الإخراج متاحًا. ويُنصَح بأن يعرض برنامج التشغيل رمز الخطأ GENERAL_FAILURE في حال تعذُّر إعداد النموذج أو تنفيذه.

نطاقات الذاكرة

بالنسبة إلى الأجهزة التي تعمل بالإصدار 11 من نظام التشغيل Android أو الإصدارات الأحدث، تتوافق واجهة 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.

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

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

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

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