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

بدءًا من نظام التشغيل 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 دائمًا مؤشرات ملفات ذاكرة التخزين المؤقت ذات إذن القراءة والكتابة.

الأمان

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

تتمثل إحدى طرق القيام بذلك في أن يحتفظ السائق بخريطة من الرمز المميز إلى تجزئة تشفيرية لذاكرة التخزين المؤقت للنموذج. يمكن لبرنامج التشغيل تخزين الرمز المميّز والرمز المميّز لملف التخزين المؤقت للنموذج عند حفظ عملية التجميع في ذاكرة التخزين المؤقت. عند استرداد عملية التجميع من ذاكرة التخزين المؤقت، يتحقّق برنامج التشغيل من التجزئة الجديدة لذاكرة التخزين المؤقت للنموذج باستخدام الرمز المميّز المسجَّل وزوج التجزئة. وينبغي أن يستمر هذا التعيين في جميع عمليات إعادة تشغيل النظام. يمكن لبرنامج التشغيل استخدام خدمة ملف تخزين مفاتيح 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، يتحمّل العميل مسؤولية تحرير العناصر المخزّنة مؤقتًا عندما لا تكون مطلوبة.