ตั้งแต่ Android 10 เป็นต้นไป Neural Networks API (NNAPI) จะมีฟังก์ชันที่รองรับการแคชรายการต่างๆ ของการคอมไพล์ ซึ่งจะช่วยลดเวลาที่ใช้ในการคอมไพล์เมื่อแอปเริ่มทำงาน เมื่อใช้ฟังก์ชันการแคชนี้ ไดรเวอร์ไม่จำเป็นต้องจัดการหรือล้างไฟล์ที่แคชไว้ นี่เป็นฟีเจอร์เสริมที่ใช้ร่วมกับ NN HAL 1.2 ได้ ดูข้อมูลเพิ่มเติมเกี่ยวกับฟังก์ชันนี้ได้ที่
ANeuralNetworksCompilation_setCaching
ไดรเวอร์ยังใช้การแคชคอมไพล์โดยไม่ขึ้นอยู่กับ NNAPI ได้อีกด้วย โดยนำข้อมูลนี้ไปใช้งานได้ไม่ว่าจะใช้ฟีเจอร์การแคช NNAPI NDK และ HAL หรือไม่ AOSP มีไลบรารียูทิลิตีระดับต่ำ (เครื่องมือแคช) ดูข้อมูลเพิ่มเติมได้ที่การใช้เครื่องมือแคช
ภาพรวมของเวิร์กโฟลว์
ส่วนนี้จะอธิบายเวิร์กโฟลว์ทั่วไปที่มีการใช้ฟีเจอร์การแคชการคอมไพล์
ข้อมูลที่แคชไว้และการพบข้อมูลในแคช
- แอปส่งไดเรกทอรีการแคชและ checksum เฉพาะของโมเดล
- รันไทม์ NNAPI จะค้นหาไฟล์แคชตาม checksum ค่ากำหนดการดำเนินการ และผลลัพธ์การแบ่งพาร์ติชัน จากนั้นค้นหาไฟล์
- ซึ่ง NNAPI จะเปิดไฟล์แคชและส่งแฮนเดิลไปยังไดรเวอร์ด้วย
prepareModelFromCache
- ไดรเวอร์จะเตรียมโมเดลโดยตรงจากไฟล์แคชและส่งคืนโมเดลที่เตรียมไว้
ข้อมูลแคชที่ระบุและแคชไม่ตรง
- แอปจะส่งการตรวจสอบข้อผิดพลาดเฉพาะของโมเดลและไดเรกทอรีการแคช
- รันไทม์ NNAPI จะค้นหาไฟล์แคชตามการตรวจสอบผลรวม ค่ากำหนดการเรียกใช้ และผลลัพธ์การแบ่งพาร์ติชัน แต่ไม่พบไฟล์แคช
- NNAPI สร้างไฟล์แคชที่ว่างเปล่าโดยอิงตามการตรวจสอบข้อผิดพลาด ค่ากำหนดการดำเนินการ และการแบ่งพาร์ติชัน จากนั้นจะเปิดไฟล์แคชและส่งแฮนเดิลและโมเดลไปยังไดรเวอร์ด้วย
prepareModel_1_2
- ไดรเวอร์คอมไพล์โมเดล เขียนข้อมูลการแคชไปยังไฟล์แคช และแสดงผลโมเดลที่เตรียมไว้
ไม่ได้ระบุข้อมูลแคช
- แอปเรียกใช้การคอมไพล์โดยไม่ระบุข้อมูลการแคช
- แอปไม่ได้ให้ข้อมูลใดๆ ที่เกี่ยวข้องกับการแคช
- รันไทม์ NNAPI จะส่งโมเดลไปยังไดรเวอร์ด้วย
prepareModel_1_2
- ไดรเวอร์คอมไพล์โมเดลและส่งโมเดลที่เตรียมไว้คืน
ข้อมูลแคช
ข้อมูลการแคชที่ให้ไว้แก่ผู้ขับประกอบด้วยโทเค็นและแฮนเดิลไฟล์แคช
โทเค็น
โทเค็นคือโทเค็นการแคชความยาว Constant::BYTE_SIZE_OF_CACHE_TOKEN
ที่ระบุโมเดลที่จัดเตรียมไว้ ระบบจะระบุโทเค็นเดียวกันเมื่อบันทึกไฟล์แคชด้วย prepareModel_1_2
และดึงโมเดลที่เตรียมไว้ด้วย prepareModelFromCache
ไคลเอ็นต์ของผู้ขับควรเลือกโทเค็นที่มีอัตราการชนกันต่ำ ไดรเวอร์ตรวจไม่พบการชนกันของโทเค็น การชนกันจะทําให้การดำเนินการไม่สําเร็จหรือทํางานสําเร็จแต่ให้ค่าเอาต์พุตที่ไม่ถูกต้อง
แฮนเดิลไฟล์แคช (ไฟล์แคช 2 ประเภท)
ไฟล์แคช 2 ประเภท ได้แก่ แคชข้อมูลและแคชโมเดล
- แคชข้อมูล: ใช้สำหรับการแคชข้อมูลคงที่ รวมถึงบัฟเฟอร์ Tensor ที่ประมวลผลล่วงหน้าและเปลี่ยนรูปแบบแล้ว การแก้ไขแคชข้อมูลไม่ควรส่งผลเสียมากกว่าการสร้างค่าเอาต์พุตที่ไม่ถูกต้องในเวลาที่ดำเนินการ
- แคชโมเดล: ใช้สำหรับแคชข้อมูลที่ละเอียดอ่อนด้านความปลอดภัย เช่น โค้ดเครื่องที่คอมไพล์แล้วซึ่งสามารถเรียกใช้ได้ในรูปแบบไบนารีของอุปกรณ์ การแก้ไขแคชของโมเดลอาจส่งผลต่อพฤติกรรมการดำเนินการของไดรเวอร์ และไคลเอ็นต์ที่เป็นอันตรายอาจใช้ประโยชน์จากส่วนนี้เพื่อดำเนินการนอกเหนือจากสิทธิ์ที่ให้ไว้ ดังนั้น ไดรเวอร์ต้องตรวจสอบว่าแคชของโมเดลเสียหายหรือไม่ก่อนที่จะเตรียมโมเดลจากแคช โปรดดูข้อมูลเพิ่มเติมที่ความปลอดภัย
โปรแกรมควบคุมต้องตัดสินใจว่าจะกระจายข้อมูลแคชระหว่างไฟล์แคช 2 ประเภทอย่างไร และรายงานจํานวนไฟล์แคชที่จําเป็นสําหรับแต่ละประเภทด้วย 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
และอ่าน/อัปเดตเนื้อหาแคชในภายหลัง - ใช้ตรรกะการล็อกไฟล์นอกการเรียกการคอมไพล์ปกติ เพื่อป้องกันไม่ให้มีการเขียนพร้อมกันกับการอ่านหรือการเขียนอื่น
ติดตั้งเครื่องมือแคช
นอกเหนือจากอินเทอร์เฟซการแคชการคอมไพล์ NN HAL 1.2 แล้ว คุณยังค้นหาไลบรารียูทิลิตีการแคชได้ในไดเรกทอรี frameworks/ml/nn/driver/cache
ด้วย ไดเรกทอรีย่อย nnCache
มีโค้ดพื้นที่เก็บข้อมูลถาวรสำหรับไดรเวอร์เพื่อใช้แคชการคอมไพล์โดยไม่ต้องใช้ฟีเจอร์การแคช NNAPI รูปแบบการแคชคอมไพล์นี้สามารถใช้กับ NN HAL เวอร์ชันใดก็ได้ หากไดรเวอร์เลือกใช้การแคชโดยยกเลิกการเชื่อมต่อกับอินเทอร์เฟซ HAL ไดรเวอร์ก็มีหน้าที่เพิ่มพื้นที่ว่างในอาร์ติแฟกต์ที่แคชไว้เมื่อไม่จำเป็นต้องใช้อีกต่อไป