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

بدءًا من 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، يكون برنامج التشغيل مسؤولاً عن تحرير العناصر المخزنة مؤقتًا عندما لا تكون هناك حاجة إليها.