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

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

מנהל ההתקן יכול גם להטמיע שמירה במטמון של הידור ללא תלות ב-NNAPI. הזה או שימוש בתכונות שמירה במטמון NNAPI NDK ו-HAL לא. 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. הלקוח של הנהג צריך לבחור אסימון עם שיעור התנגשויות נמוך. הנהג לא יכול לזהות התנגשות בין אסימונים. התנגשות יובילו לכשל או לביצוע מוצלח ערכי פלט שגויים.

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

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

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

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

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

אבטחה

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

דרך אחת לעשות זאת היא על ידי הנהג לתחזק מפה מהאסימון גיבוב (hash) קריפטוגרפי של המטמון של המודל. הנהג יכול לאחסן את האסימון גיבוב (hash) של המטמון של המודל כששומרים את הידור במטמון. הנהג/ת מבצע/ת בדיקה את הגיבוב החדש של המטמון של המודל עם האסימון המוקלט וצמד הגיבוב, מאחזר את ההידור מהמטמון. המיפוי הזה אמור להישאר קבוע גם אחרי הפעלות מחדש של המערכת. הנהג יכול להשתמש Android keystore service, ספריית השירות ב- framework/ml/nn/driver/cache, או כל מנגנון מתאים אחר להטמעת מנהל מיפוי. עם הנהג/ת יש לאתחל מחדש את מנהל המיפוי הזה כדי למנוע הכנת המטמון מגרסה קודמת.

כדי למנוע התקפות מסוג time-of-check to time-of-use (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. תרחישים לדוגמה:

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

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

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

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

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