การแคชการคอมไพล์

จาก Android 10, Neural Networks API (NNAPI) ซึ่งมีฟังก์ชันต่างๆ เพื่อช่วยสนับสนุน การแคชอาร์ติแฟกต์สำหรับการคอมไพล์ ซึ่งช่วยลดเวลาที่ใช้ในการคอมไพล์ เมื่อแอปเริ่มทำงาน เมื่อใช้ฟังก์ชันการแคชนี้ ไดรเวอร์จะไม่ ต้องจัดการหรือล้างไฟล์ที่แคชไว้ นี่เป็นฟีเจอร์เสริมที่ สามารถใช้ร่วมกับ NN HAL 1.2 ได้ หากต้องการข้อมูลเพิ่มเติมเกี่ยวกับฟังก์ชันนี้ ดู ANeuralNetworksCompilation_setCaching

ไดรเวอร์ยังใช้การแคชคอมไพล์โดยไม่ขึ้นอยู่กับ NNAPI ได้อีกด้วย ช่วงเวลานี้ สามารถนำไปใช้ได้ ไม่ว่าจะมีการใช้ฟีเจอร์การแคช NNAPI NDK และ HAL หรือ ไม่ได้ AOSP มีไลบรารียูทิลิตีระดับต่ำ (เครื่องมือแคช) สำหรับข้อมูลเพิ่มเติม โปรดดูการใช้เครื่องมือแคช

ภาพรวมเวิร์กโฟลว์

ส่วนนี้จะอธิบายเวิร์กโฟลว์ทั่วไปที่มีฟีเจอร์การแคชการคอมไพล์ ที่มีการนำไปใช้

ข้อมูลแคชที่ระบุและพบแคช

  1. แอปส่งไดเรกทอรีการแคชและ checksum เฉพาะของโมเดล
  2. รันไทม์ของ NNAPI จะค้นหาไฟล์แคชตาม checksum ค่ากำหนดการดำเนินการ และผลลัพธ์การแบ่งพาร์ติชัน แล้วค้นหาไฟล์ดังกล่าว
  3. NNAPI จะเปิดไฟล์แคชและส่งแฮนเดิลไปยังไดรเวอร์ กับ prepareModelFromCache
  4. ไดรเวอร์จะเตรียมโมเดลโดยตรงจากไฟล์แคชและส่งคืน โมเดลที่เตรียมไว้

ข้อมูลแคชที่ระบุและแคชหายไป

  1. แอปส่งการตรวจสอบข้อผิดพลาดเฉพาะของโมเดลและการแคช ไดเรกทอรี
  2. รันไทม์ NNAPI จะค้นหาไฟล์การแคชตาม checksum ค่ากำหนดการดำเนินการ และผลลัพธ์การแบ่งพาร์ติชัน และไม่พบ ไฟล์แคช
  3. NNAPI สร้างไฟล์แคชที่ว่างเปล่าตามการตรวจสอบข้อผิดพลาด (Checksum) และการแบ่งพาร์ติชัน จะเปิดไฟล์แคชและส่ง แฮนเดิลและโมเดลให้กับคนขับ prepareModel_1_2
  4. ไดรเวอร์คอมไพล์โมเดล เขียนข้อมูลการแคชไปยังแคช และแสดงผลโมเดลที่เตรียมไว้

ไม่ได้ระบุข้อมูลแคช

  1. แอปเรียกใช้การคอมไพล์โดยไม่ให้ข้อมูลการแคชใดๆ
  2. แอปไม่ได้ให้ข้อมูลใดๆ ที่เกี่ยวข้องกับการแคช
  3. รันไทม์ NNAPI จะส่งโมเดลไปยังไดรเวอร์ด้วย prepareModel_1_2
  4. ไดรเวอร์คอมไพล์โมเดลและส่งโมเดลที่เตรียมไว้คืน

ข้อมูลแคช

ข้อมูลการแคชที่ให้ไว้กับผู้ขับประกอบด้วยโทเค็นและ แฮนเดิลไฟล์แคช

โทเค็น

โทเค็น เป็นโทเค็นการแคชความยาว Constant::BYTE_SIZE_OF_CACHE_TOKEN ที่ระบุโมเดลที่เตรียมไว้ ระบบจะระบุโทเค็นเดียวกันเมื่อบันทึก แคชไฟล์ด้วย prepareModel_1_2 และดึงข้อมูลโมเดลที่เตรียมไว้ด้วย prepareModelFromCache ไคลเอ็นต์ของผู้ขับขี่ควรเลือกโทเค็นที่มี มีการชนกันต่ำ คนขับไม่พบการชนกันของโทเค็น การชนกัน ส่งผลให้การดำเนินการล้มเหลว หรือในการดำเนินการที่สำเร็จซึ่ง ค่าเอาต์พุตไม่ถูกต้อง

แฮนเดิลไฟล์แคช (ไฟล์แคช 2 ประเภท)

ไฟล์แคช 2 ประเภทคือแคชข้อมูลและแคชโมเดล

  • แคชข้อมูล: ใช้สำหรับแคชข้อมูลคงที่ รวมถึงข้อมูลที่ประมวลผลล่วงหน้าและ ที่เปลี่ยนรูปแบบบัฟเฟอร์ Tensor การแก้ไขแคชข้อมูลไม่ควร ส่งผลให้เกิดผลลัพธ์แย่กว่าการสร้างค่าเอาต์พุตที่ไม่ถูกต้องขณะดำเนินการ
  • แคชโมเดล: ใช้สำหรับแคชข้อมูลที่อ่อนไหวต่อความปลอดภัย เช่น ข้อมูลที่คอมไพล์แล้ว รหัสเครื่องสั่งการในรูปแบบไบนารีของระบบของอุปกรณ์ ต การแก้ไขแคชโมเดลอาจส่งผลต่อการดำเนินการของไดรเวอร์ จนเกินกำหนด แล้วไคลเอ็นต์ที่ประสงค์ร้ายก็อาจใช้สิ่งนี้เพื่อดำเนินการ สิทธิ์ที่ให้ไว้ ดังนั้น ไดรเวอร์ต้องตรวจสอบว่าแคชโมเดล เสียหายก่อนที่จะเตรียมโมเดลจากแคช สำหรับข้อมูลเพิ่มเติม ดูความปลอดภัย

ไดรเวอร์ต้องเลือกวิธีการกระจายข้อมูลแคชระหว่าง ประเภทไฟล์แคช และรายงานจำนวนไฟล์แคชที่จำเป็นสำหรับแต่ละประเภท กับ 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;
    }
}

Use Case ขั้นสูง

ในกรณีการใช้งานขั้นสูงบางกรณี ไดรเวอร์ต้องใช้สิทธิ์เข้าถึงเนื้อหาแคช (อ่านหรือเขียน) หลังการเรียกการคอมไพล์ ตัวอย่างกรณีการใช้งานมีดังนี้

  • การคอมไพล์แบบทันท่วงที: การคอมไพล์จะล่าช้าจนกว่า การดำเนินการครั้งแรก
  • การคอมไพล์แบบหลายขั้นตอน: การคอมไพล์แบบรวดเร็วเริ่มต้น และการรวบรวมเนื้อหาที่เพิ่มประสิทธิภาพ (ไม่บังคับ) จะดำเนินการในภายหลัง โดยขึ้นอยู่กับความถี่ในการใช้งาน

หากต้องการเข้าถึงเนื้อหาแคช (อ่านหรือเขียน) หลังการเรียกการคอมไพล์ โปรดตรวจสอบว่า ที่คนขับจะ:

  • ทำสำเนาแฮนเดิลไฟล์ระหว่างการเรียกใช้ prepareModel_1_2 หรือ prepareModelFromCache และอ่าน/อัปเดตแคช เนื้อหาในภายหลัง
  • ใช้ตรรกะการล็อกไฟล์นอกการเรียกใช้การคอมไพล์ปกติ เพื่อป้องกันไม่ให้การเขียนเกิดขึ้นพร้อมกันกับการเขียน (Read) หรือการเขียนอื่นๆ

ติดตั้งเครื่องมือแคช

นอกจากอินเทอร์เฟซการแคชการคอมไพล์ NN HAL 1.2 แล้ว คุณยังสามารถค้นหา ในไลบรารีของยูทิลิตีแคช frameworks/ml/nn/driver/cache ไดเรกทอรี nnCache ไดเรกทอรีย่อยมีโค้ดพื้นที่เก็บข้อมูลถาวรสำหรับให้ไดรเวอร์ใช้ การแคชการคอมไพล์โดยไม่ต้องใช้ฟีเจอร์การแคช NNAPI แบบฟอร์มนี้ การแคชคอมไพล์สามารถใช้กับ NN HAL เวอร์ชันใดก็ได้ หาก ผู้ขับขี่เลือกที่จะใช้การแคช โดยตัดการเชื่อมต่อจากอินเทอร์เฟซ HAL คนขับคือ ซึ่งจะทำหน้าที่ตรึงอาร์ติแฟกต์ที่แคชไว้เมื่อไม่จำเป็นต้องใช้อีกต่อไป