من Android 10 ، توفر واجهة برمجة تطبيقات الشبكات العصبية (NNAPI) وظائف لدعم التخزين المؤقت لأعمال التجميع ، مما يقلل من الوقت المستخدم للتجميع عند بدء تشغيل التطبيق. باستخدام وظيفة التخزين المؤقت هذه ، لا يحتاج برنامج التشغيل إلى إدارة الملفات المخزنة مؤقتًا أو تنظيفها. هذه ميزة اختيارية يمكن تنفيذها باستخدام NN HAL 1.2. لمزيد من المعلومات حول هذه الوظيفة ، راجع ANeuralNetworksCompilation_setCaching
.
يمكن للسائق أيضًا تنفيذ التخزين المؤقت للترجمة المستقلة عن NNAPI. يمكن تنفيذ ذلك سواء تم استخدام ميزات التخزين المؤقت NNAPI NDK و HAL أم لا. يوفر AOSP مكتبة أدوات مساعدة منخفضة المستوى (محرك تخزين مؤقت). لمزيد من المعلومات ، راجع تنفيذ محرك التخزين المؤقت .
نظرة عامة على سير العمل
يصف هذا القسم تدفقات العمل العامة مع تنفيذ ميزة التخزين المؤقت للترجمة.
قدمت معلومات التخزين المؤقت وذاكرة التخزين المؤقت
- يمرر التطبيق دليل التخزين المؤقت ومجموعًا اختباريًا فريدًا للنموذج.
- يبحث وقت تشغيل NNAPI عن ملفات ذاكرة التخزين المؤقت بناءً على المجموع الاختباري وتفضيل التنفيذ ونتائج التقسيم والعثور على الملفات.
- يفتح NNAPI ملفات ذاكرة التخزين المؤقت ويمرر المقابض إلى برنامج التشغيل باستخدام
prepareModelFromCache
. - يقوم السائق بإعداد النموذج مباشرة من ملفات ذاكرة التخزين المؤقت ويعيد النموذج المعد.
معلومات التخزين المؤقت المقدمة وذاكرة التخزين المؤقت مفقودة
- يمرر التطبيق مجموع اختباري فريد للنموذج ودليل التخزين المؤقت.
- يبحث وقت تشغيل NNAPI عن ملفات التخزين المؤقت بناءً على المجموع الاختباري وتفضيل التنفيذ ونتائج التقسيم ولا يعثر على ملفات ذاكرة التخزين المؤقت.
- يقوم NNAPI بإنشاء ملفات ذاكرة تخزين مؤقت فارغة بناءً على المجموع الاختباري وتفضيل التنفيذ والتقسيم ويفتح ملفات ذاكرة التخزين المؤقت ويمرر المقابض والنموذج إلى برنامج التشغيل باستخدام
prepareModel_1_2
. - يقوم برنامج التشغيل بتجميع النموذج ، وكتابة معلومات التخزين المؤقت في ملفات ذاكرة التخزين المؤقت ، وإرجاع النموذج المعد.
لم يتم توفير معلومات التخزين المؤقت
- يستدعي التطبيق التجميع دون تقديم أي معلومات عن التخزين المؤقت.
- لا يمر التطبيق بأي شيء متعلق بالتخزين المؤقت.
- يمرر وقت تشغيل NNAPI النموذج إلى السائق باستخدام
prepareModel_1_2
. - يقوم السائق بتجميع النموذج وإرجاع النموذج المعد.
معلومات التخزين المؤقت
تتكون معلومات التخزين المؤقت التي يتم توفيرها لبرنامج التشغيل من مقابض ملفات الرمز المميز وذاكرة التخزين المؤقت.
رمز
الرمز المميز هو رمز تخزين مؤقت بطول 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 ، فسيكون السائق مسؤولاً عن تحرير القطع الأثرية المخزنة مؤقتًا عندما لا تكون هناك حاجة إليها.