कंपाइलेशन कैश मेमोरी

Android 10 से, Neural Networks API (NNAPI) की मदद से कंपाइलेशन आर्टफ़ैक्ट को कैश मेमोरी में सेव करने की सुविधा दी जाती है. इससे, ऐप्लिकेशन के शुरू होने पर, कंपाइलेशन में लगने वाला समय कम होता है. कैश मेमोरी में सेव करने की इस सुविधा का इस्तेमाल करने पर, ड्राइवर को कैश मेमोरी में सेव की गई फ़ाइलों को मैनेज करने या हटाने की ज़रूरत नहीं होती. यह एक वैकल्पिक सुविधा है, जिसे NN HAL 1.2 के साथ लागू किया जा सकता है. इस फ़ंक्शन के बारे में ज़्यादा जानकारी के लिए, ANeuralNetworksCompilation_setCaching देखें.

ड्राइवर, एनएनएपीआई के अलावा किसी दूसरे तरीके से भी कंपाइलेशन कैश मेमोरी लागू कर सकता है. NNAPI एनडीके और एचएएल कैश मेमोरी की सुविधाओं का इस्तेमाल किया जा रहा है या नहीं, इसे लागू किया जा सकता है. एओएसपी, एक लो-लेवल यूटिलिटी लाइब्रेरी (कैश मेमोरी में सेव किया जाने वाला इंजन) उपलब्ध कराता है. ज़्यादा जानकारी के लिए, कैशिंग इंजन लागू करना देखें.

वर्कफ़्लो की खास जानकारी

इस सेक्शन में, कंपाइलेशन कैश मेमोरी की सुविधा लागू करके सामान्य वर्कफ़्लो के बारे में बताया गया है.

कैश मेमोरी की जानकारी दी गई और कैश मेमोरी हिट हुई

  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 सबडायरेक्ट्री में, ड्राइवर के लिए परसिस्टेंट स्टोरेज कोड होता है, ताकि एनएनएपीआई कैश मेमोरी की सुविधाओं का इस्तेमाल किए बिना, कंपाइलेशन कैश मेमोरी लागू की जा सके. इस तरह के कंपाइलेशन कैश को NN HAL के किसी भी वर्शन के साथ लागू किया जा सकता है. अगर ड्राइवर, एचएएल इंटरफ़ेस से कैश मेमोरी में सेव होने का विकल्प चुनता है, तो कैश मेमोरी में सेव किए गए आर्टफ़ैक्ट की ज़रूरत न होने पर उन्हें खाली करने की ज़िम्मेदारी ड्राइवर की होती है.