التخزين المؤقت للتجميع

بدءًا من نظام التشغيل Android 10، توفّر واجهة برمجة تطبيقات الشبكات العصبية (NNAPI) وظائف لإتاحة التخزين المؤقت لعناصر التجميع، ما يقلّل من الوقت المُستغرَق في التجميع عند بدء تشغيل التطبيق. وباستخدام وظيفة التخزين المؤقت هذه، لا يحتاج برنامج التشغيل إلى إدارة الملفات المخزنة مؤقتًا أو تنظيفها. هذه ميزة اختيارية يمكن تنفيذها باستخدام NN HAL 1.2. للحصول على مزيد من المعلومات حول هذه الدالة، يمكنك الاطّلاع على ANeuralNetworksCompilation_setCaching.

يمكن لبرنامج التشغيل أيضًا تنفيذ التخزين المؤقت للتجميع البرمجي بشكل مستقل عن NNAPI. ويمكن تنفيذ ذلك سواء تم استخدام ميزات التخزين المؤقت لـ NNAPI NDK وHAL أم لا. يوفر AOSP مكتبة من الخدمات منخفضة المستوى (محرك تخزين مؤقت). لمزيد من المعلومات، يُرجى الاطّلاع على تنفيذ محرك تخزين مؤقت.

نظرة عامة على سير العمل

يصف هذا القسم عمليات سير العمل العامة مع تنفيذ ميزة التخزين المؤقت للتجميع.

تم تقديم معلومات ذاكرة التخزين المؤقت ونتيجة ذاكرة التخزين المؤقت

  1. يجتاز التطبيق دليل تخزين مؤقت ومجموع اختباري فريد للنموذج.
  2. يبحث بيئة تشغيل NNAPI عن ملفات ذاكرة التخزين المؤقت استنادًا إلى المجموع الاختباري وتفضيل التنفيذ ونتيجة التقسيم ويبحث عن الملفات.
  3. تفتح NNAPI ملفات ذاكرة التخزين المؤقت وتنقل الأسماء المعرّفة إلى برنامج التشغيل من خلال prepareModelFromCache.
  4. ويعدّ برنامج التشغيل النموذج مباشرةً من ملفات ذاكرة التخزين المؤقت ويعرض النموذج الذي تم إعداده.

تم تقديم معلومات ذاكرة التخزين المؤقت وذاكرة التخزين المؤقت غير موجودة

  1. يجتاز التطبيق مجموعًا اختباريًا فريدًا للنموذج ودليل التخزين المؤقت.
  2. يبحث بيئة تشغيل NNAPI عن ملفات التخزين المؤقت استنادًا إلى المجموع الاختباري وتفضيل التنفيذ ونتيجة التقسيم ولا يجد ملفات ذاكرة التخزين المؤقت.
  3. تنشئ NNAPI ملفات ذاكرة تخزين مؤقتة فارغة استنادًا إلى المجموع الاختباري وتفضيل التنفيذ والتقسيم، وتفتح ملفات ذاكرة التخزين المؤقت، وتمرر الأسماء المعرِّفة والنموذج إلى برنامج التشغيل باستخدام prepareModel_1_2.
  4. يجمع برنامج التشغيل النموذج ويكتب معلومات التخزين المؤقت في ملفات ذاكرة التخزين المؤقت ويعرض النموذج المُعدّ.

لم يتم توفير معلومات ذاكرة التخزين المؤقت

  1. يستدعي التطبيق التجميع بدون توفير أي معلومات عن التخزين المؤقت.
  2. لا يعمل التطبيق على تمرير أي شيء مرتبط بالتخزين المؤقت.
  3. ينقل بيئة تشغيل NNAPI النموذج إلى برنامج التشغيل باستخدام prepareModel_1_2.
  4. يقوم برنامج التشغيل بتجميع النموذج وعرض النموذج المعد.

معلومات ذاكرة التخزين المؤقت

تتكوّن معلومات التخزين المؤقت التي يتم توفيرها لبرنامج التشغيل من رمز مميز ومؤشرات ملفات ذاكرة التخزين المؤقت.

الرمز المميز

الرمز المميّز هو رمز مميّز للتخزين المؤقت بطول Constant::BYTE_SIZE_OF_CACHE_TOKEN يحدّد النموذج المُعدّ. يتم توفير الرمز المميّز نفسه عند حفظ ملفات ذاكرة التخزين المؤقت باستخدام prepareModel_1_2 واسترداد النموذج الذي تم إعداده باستخدام prepareModelFromCache. يجب على عميل السائق اختيار رمز مميز بمعدل منخفض للاصطدام. لا يمكن للسائق اكتشاف تضارب الرمز المميز. ينتج عن التصادم عملية تنفيذ فاشلة أو تنفيذ ناجح ينتج عنه قيم إخراج غير صحيحة.

مؤشرات ملفات ذاكرة التخزين المؤقت (نوعان من ملفات ذاكرة التخزين المؤقت)

نوعا ملفات ذاكرة التخزين المؤقت هما ذاكرة التخزين المؤقت للبيانات وذاكرة التخزين المؤقت للنماذج.

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

يجب أن يحدِّد برنامج التشغيل كيفية توزيع معلومات ذاكرة التخزين المؤقت بين نوعَي ملفات ذاكرة التخزين المؤقت، والإبلاغ عن عدد ملفات ذاكرة التخزين المؤقت اللازمة لكل نوع باستخدام getNumberOfCacheFilesNeeded.

يفتح بيئة تشغيل NNAPI دائمًا مؤشرات ملفات ذاكرة التخزين المؤقت ذات إذن القراءة والكتابة.

الأمان

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

تتمثل إحدى طرق القيام بذلك في أن يحتفظ السائق بخريطة من الرمز المميز إلى تجزئة تشفيرية لذاكرة التخزين المؤقت للنموذج. يمكن لبرنامج التشغيل تخزين الرمز المميز وتجزئة ذاكرة التخزين المؤقت لطرازه عند حفظ التحويل البرمجي إلى ذاكرة التخزين المؤقت. عند استرداد عملية التجميع من ذاكرة التخزين المؤقت، يتحقّق برنامج التشغيل من التجزئة الجديدة لذاكرة التخزين المؤقت للنموذج باستخدام الرمز المميّز المسجَّل وزوج التجزئة. وينبغي أن يستمر هذا التعيين في جميع عمليات إعادة تشغيل النظام. يمكن لبرنامج التشغيل استخدام خدمة ملف تخزين مفاتيح Android أو مكتبة الأدوات المساعدة في framework/ml/nn/driver/cache أو أي آلية أخرى مناسبة لتنفيذ مدير ربط. عند تحديث برنامج التشغيل، يجب إعادة إعداد مدير التعيين هذا لمنع إعداد ملفات ذاكرة التخزين المؤقت من إصدار سابق.

لمنع هجمات وقت التحقق من وقت الاستخدام (TOCTOU)، على برنامج التشغيل أن يحسب قيمة التجزئة المسجّلة قبل الحفظ في الملف وحساب التجزئة الجديدة بعد نسخ محتوى الملف إلى مخزن مؤقت داخلي.

يوضح نموذج التعليمة البرمجية هذا كيفية تنفيذ هذا المنطق.

bool saveToCache(const sp<V1_2::IPreparedModel> preparedModel,
                 const hidl_vec<hidl_handle>& modelFds, const hidl_vec<hidl_handle>& dataFds,
                 const HidlToken& token) {
    // Serialize the prepared model to internal buffers.
    auto buffers = serialize(preparedModel);

    // This implementation detail is important: the cache hash must be computed from internal
    // buffers instead of cache files to prevent time-of-check to time-of-use (TOCTOU) attacks.
    auto hash = computeHash(buffers);

    // Store the {token, hash} pair to a mapping manager that is persistent across reboots.
    CacheManager::get()->store(token, hash);

    // Write the cache contents from internal buffers to cache files.
    return writeToFds(buffers, modelFds, dataFds);
}

sp<V1_2::IPreparedModel> prepareFromCache(const hidl_vec<hidl_handle>& modelFds,
                                          const hidl_vec<hidl_handle>& dataFds,
                                          const HidlToken& token) {
    // Copy the cache contents from cache files to internal buffers.
    auto buffers = readFromFds(modelFds, dataFds);

    // This implementation detail is important: the cache hash must be computed from internal
    // buffers instead of cache files to prevent time-of-check to time-of-use (TOCTOU) attacks.
    auto hash = computeHash(buffers);

    // Validate the {token, hash} pair by a mapping manager that is persistent across reboots.
    if (CacheManager::get()->validate(token, hash)) {
        // Retrieve the prepared model from internal buffers.
        return deserialize<V1_2::IPreparedModel>(buffers);
    } else {
        return nullptr;
    }
}

حالات الاستخدام المتقدّمة

في بعض حالات الاستخدام المتقدمة، يتطلب السائق الوصول إلى محتوى ذاكرة التخزين المؤقت (قراءة أو كتابة) بعد استدعاء التجميع. تشمل أمثلة حالات الاستخدام ما يلي:

  • تجميع المحتوى في الوقت المناسب: يتأخر نقل البيانات المجمّعة إلى أن يتم تنفيذ العملية الأولى.
  • تجميع المحتوى المتعدّد المراحل: يتم إجراء تجميع سريع في البداية، ثم إنشاء تجميع اختياري محسَّن في وقت لاحق، بناءً على معدّل تكرار الاستخدام.

للوصول إلى محتوى ذاكرة التخزين المؤقت (قراءة أو كتابة) بعد استدعاء التجميع، تأكد من أن برنامج التشغيل:

  • يتم إنشاء نسخة طبق الأصل من الأسماء المعرِّفة للملفات أثناء استدعاء prepareModel_1_2 أو prepareModelFromCache مع قراءة محتوى ذاكرة التخزين المؤقت أو تعديله في وقت لاحق.
  • لتنفيذ منطق قفل الملف خارج استدعاء التجميع العادي لمنع حدوث كتابة بالتزامن مع قراءة أو كتابة أخرى.

تنفيذ محرك التخزين المؤقت

بالإضافة إلى واجهة التخزين المؤقت للتجميعات NN HAL 1.2، يمكنك أيضًا العثور على مكتبة أدوات للتخزين المؤقت في دليل frameworks/ml/nn/driver/cache. يحتوي الدليل الفرعي nnCache على رمز تخزين ثابت لبرنامج التشغيل لتنفيذ التخزين المؤقت للتجميع بدون استخدام ميزات التخزين المؤقت في NNAPI. يمكن تنفيذ هذا الشكل من التخزين المؤقت للتجميع مع أي إصدار من NN HAL. إذا اختار برنامج التشغيل تنفيذ التخزين المؤقت غير المرتبط بواجهة HAL، يكون السائق مسؤولاً عن تحرير العناصر المخزّنة مؤقتًا عندما لا تكون هناك حاجة إليها.