שמירה במטמון של הידור

החל מ-Android 10, ‏Neural Networks API‏ (NNAPI) מספק פונקציות שתומכות בשמירת פריטים של הידור במטמון, וכך מפחית את משך הזמן הנדרש לצורך הידור כשאפליקציה מופעלת. בעזרת פונקציונליות האחסון במטמון, הנהג לא צריך לנהל או לנקות את הקבצים ששמורים במטמון. זוהי תכונה אופציונלית עם NN HAL 1.2. כדי לקבל מידע נוסף על הפונקציה הזו, לראות ANeuralNetworksCompilation_setCaching

מנהל ההתקן יכול גם להטמיע שמירה במטמון של הידור ללא תלות ב-NNAPI. אפשר להטמיע את התכונה הזו גם אם משתמשים בתכונות של NNAPI NDK ו-HAL caching וגם אם לא. AOSP מספק ספריית עזר ברמה נמוכה (מנוע לשמירה במטמון). לקבלת מידע נוסף מידע נוסף מופיע במאמר יישום של מנוע שמירה במטמון.

סקירה כללית של תהליך העבודה

בקטע הזה מתוארים תהליכי עבודה כלליים עם התכונה של שמירה במטמון של הידור. .

מידע שסופק מהמטמון והצלחה במטמון

  1. האפליקציה מעבירה ספריית מטמון ותוצאת סיכום ייחודית למודל.
  2. סביבת זמן הריצה של NNAPI מחפשת את קובצי המטמון על סמך סיכום הביקורות, העדפת הביצוע והתוצאה של חלוקת המחיצות, ומוצאת את הקבצים.
  3. NNAPI פותח את קובצי המטמון ומעביר את הלחצנים לנהג באמצעות prepareModelFromCache.
  4. הנהג מכין את המודל ישירות מקובצי המטמון ומחזיר את המודל את המודל המוכן.

מידע שסופק במטמון והחמצת אחסון במטמון

  1. האפליקציה מעבירה סיכום ביקורת (checksum) ייחודי למודל ספרייה של מטמון.
  2. סביבת זמן הריצה של NNAPI מחפשת את קובצי המטמון על סמך סיכום הביקורות, העדפת הביצוע והתוצאה של חלוקת המחיצות, ולא מוצאת את קובצי המטמון.
  3. NNAPI יוצר קבצי מטמון ריקים על סמך סיכום הביקורת, העדפת הביצוע והחלוקה, פותח את קבצי המטמון ומעביר את ה-handles והמודל לנהג באמצעות prepareModel_1_2.
  4. הנהג מקמפל את המודל, כותב את פרטי האחסון במטמון לקובצי המטמון ומחזיר את המודל המוכשר.

לא סופקו פרטי מטמון

  1. האפליקציה מפעילה את הידור הקוד בלי לספק מידע על שמירת נתונים במטמון.
  2. האפליקציה לא מעבירה שום דבר שקשור לשמירה במטמון.
  3. זמן הריצה של NNAPI מעביר את המודל לנהג באמצעות prepareModel_1_2
  4. מנהל ההתקן אוסף את המודל ומחזיר את המודל שהוכן.

מידע על המטמון

מידע השמירה במטמון שמסופק לנהג מורכב מאסימון כינויים של קבצים במטמון.

אסימון

אסימון הוא אסימון שמאוחסן במטמון באורך Constant::BYTE_SIZE_OF_CACHE_TOKEN שמזהה את המודל שהוכנה. אותו אסימון מסופק כששומרים את קובצי המטמון באמצעות prepareModel_1_2 ומאחזרים את המודל המוכשר באמצעות prepareModelFromCache. הלקוח של הנהג צריך לבחור אסימון עם שיעור התנגשות נמוך. הנהג לא יכול לזהות התנגשות אסימונים. התנגשויות גורמות לביצוע כושל או לביצוע מוצלח שמניב ערכים שגויים של פלט.

נקודות אחיזה לקבצים של המטמון (שני סוגים של קובצי מטמון)

שני הסוגים של קובצי מטמון הם מטמון הנתונים ומטמון של המודל.

  • מטמון נתונים: משמש לשמירה במטמון של נתונים קבועים, כולל נתונים שעברו עיבוד מראש ו מאגרי חוצץ Tensor שעברו טרנספורמציה. אם רוצים לבצע שינוי במטמון הנתונים, התוצאה תהיה פחות השפעה מאשר יצירה של ערכי פלט שגויים בזמן הביצוע בזמן האימון.
  • מטמון המודל: משמש לשמירה במטמון של נתונים רגישים שקשורים לאבטחה, כמו הידור קוד ההפעלה של המכונה בפורמט הבינארי המקורי של המכשיר. שינוי במטמון המודל עלול להשפיע על התנהגות הביצוע של הנהג, ולקוח זדוני יכול להשתמש בכך כדי לבצע פעולות מעבר להרשאה שניתנה. לכן, מנהל ההתקן צריך לבדוק אם מטמון המודל פגום לפני שהוא מכין את המודל מהמטמון. למידע נוסף, קראו את המאמר אבטחה.

הנהג צריך להחליט איך להפיץ את נתוני המטמון בין שני המכשירים שונים של קובצי מטמון, ודיווח על מספר הקבצים השמורים במטמון לכל סוג. עם getNumberOfCacheFilesNeeded

סביבת זמן הריצה של NNAPI תמיד פותחת כינויים של קבצים במטמון עם קריאה וכתיבה הרשאה.

אבטחה

בשמירה במטמון של מודל, מטמון המודל עשוי להכיל מידע רגיש מבחינת אבטחה, כקוד של מכונה הפעלה מקומפלת בפורמט הבינארי המקורי של המכשיר. אם לא מגינים על המטמון של המודל כראוי, שינוי במטמון עשוי להשפיע על התנהגות הביצוע של מנהל ההתקן. כי תוכן המטמון שמור באפליקציה , קובצי המטמון ניתנים לשינוי על ידי הלקוח. לקוח עם באגים עלול לגרום בטעות לפגיעה במטמון, ולקוח זדוני עלול לנצל זאת בכוונה כדי להריץ קוד לא מאומת במכשיר. בהתאם למאפיינים של המכשיר, יכול להיות שמדובר בבעיית אבטחה. לכן, לנהג צריכה להיות אפשרות לזהות פגיעה אפשרית במטמון של המודל לפני שהוא מכין את המודל מהמטמון.

דרך אחת לעשות זאת היא על ידי הנהג לתחזק מפה מהאסימון גיבוב (hash) קריפטוגרפי של המטמון של המודל. הנהג יכול לאחסן את האסימון גיבוב (hash) של המטמון של המודל כששומרים את הידור במטמון. הנהג/ת מבצע/ת בדיקה את הגיבוב החדש של המטמון של המודל עם האסימון המוקלט וצמד הגיבוב, מאחזר את ההידור מהמטמון. המיפוי הזה אמור להישאר קבוע גם אחרי הפעלות מחדש של המערכת. הנהג יכול להשתמש בשירות של מאגר המפתחות של 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;
    }
}

תרחישים מתקדמים לדוגמה

בתרחישי שימוש מתקדמים מסוימים, הנהג דורש גישה לתוכן המטמון (קריאה או כתיבה) אחרי קריאת ה-compile. תרחישים לדוגמה:

  • אוסף בדיוק בזמן: האוסף מתעכב עד הביצוע הראשון.
  • הידור רב-שלבי: בהתחלה מתבצע הידור מהיר ואיסוף מותאם אופציונלי מתבצע במועד מאוחר יותר בהתאם לתדירות השימוש.

כדי לגשת לתוכן המטמון (לקריאה או לכתיבה) אחרי קריאת ה-compilation, צריך לוודא שהדרייבר:

  • כפילות של כינויי הקובץ במהלך הפעלת prepareModel_1_2 או prepareModelFromCache וקורא/ת או מעדכן את המטמון תוכן במועד מאוחר יותר.
  • הטמעת לוגיקה של נעילת קבצים מחוץ לקריאה הרגילה של הידור, כדי למנוע כתיבת שתתבצע בו-זמנית עם קריאה או כתיבת אחרת.

הטמעת מנוע שמירה במטמון

בנוסף לממשק של NN HAL 1.2 ל-compilation caching, תוכלו למצוא גם ספריית שירותי אחסון במטמון בתיקייה frameworks/ml/nn/driver/cache. ספריית המשנה nnCache מכילה קוד אחסון מתמיד שמאפשר לנהג להטמיע מטמון של הידור בלי להשתמש בתכונות המטמון של NNAPI. הטופס הזה של אפשר להטמיע שמירה במטמון של הידור בכל גרסה של תקן NN HAL. אם הנהג בוחר להטמיע מטמון שמנותק מממשק ה-HAL, הוא אחראי לפנות את הארטיפקטים ששמורים במטמון כשאין בהם יותר צורך.