API โมเดลที่คอมไพล์แล้วของ LiteRT พร้อมให้บริการใน C++ ซึ่งช่วยให้นักพัฒนาแอป Android ควบคุมการจัดสรรหน่วยความจำและการพัฒนาในระดับต่ำได้อย่างละเอียด
ดูตัวอย่างแอปพลิเคชัน LiteRT ใน C++ ได้ที่การแบ่งกลุ่มแบบอะซิงโครนัสด้วย C++ สาธิต
เริ่มต้นใช้งาน
ทำตามขั้นตอนต่อไปนี้เพื่อเพิ่ม LiteRT Compiled Model API ลงในแอปพลิเคชัน Android
อัปเดตการกำหนดค่าบิลด์
การสร้างแอปพลิเคชัน C++ ด้วย LiteRT สำหรับการเร่งความเร็ว GPU, NPU และ CPU โดยใช้ Bazel เกี่ยวข้องกับการกำหนดกฎ cc_binary เพื่อให้มั่นใจว่าคอมโพเนนต์ที่จำเป็นทั้งหมดจะได้รับการคอมไพล์ ลิงก์ และแพ็กเกจ การตั้งค่าตัวอย่างต่อไปนี้
ช่วยให้แอปพลิเคชันเลือกหรือใช้ตัวเร่ง GPU, NPU และ CPU
แบบไดนามิกได้
องค์ประกอบสำคัญในการกำหนดค่าการสร้าง Bazel มีดังนี้
cc_binaryกฎ: นี่คือกฎพื้นฐานของ Bazel ที่ใช้กำหนดเป้าหมายที่เรียกใช้งานได้ของ C++ (เช่นname = "your_application_name")srcsแอตทริบิวต์: แสดงรายการไฟล์ต้นฉบับ C++ ของแอปพลิเคชัน (เช่นmain.ccและไฟล์อื่นๆ.ccหรือ.h)dataแอตทริบิวต์ (การขึ้นต่อกันขณะรันไทม์): แอตทริบิวต์นี้มีความสำคัญอย่างยิ่งต่อการแพ็กเกจ ไลบรารีและชิ้นงานที่แชร์ซึ่งแอปพลิเคชันโหลดขณะรันไทม์- รันไทม์หลักของ LiteRT: ไลบรารีที่ใช้ร่วมกันของ LiteRT C API หลัก (เช่น
//litert/c:litert_runtime_c_api_shared_lib) - ไลบรารีการจัดส่ง: ไลบรารีที่ใช้ร่วมกันเฉพาะผู้ให้บริการที่ LiteRT
ใช้เพื่อสื่อสารกับไดรเวอร์ฮาร์ดแวร์ (เช่น
//litert/vendors/qualcomm/dispatch:dispatch_api_so) - ไลบรารีแบ็กเอนด์ของ GPU: ไลบรารีที่ใช้ร่วมกันสำหรับการเร่งความเร็ว GPU
(เช่น
"@litert_gpu//:jni/arm64-v8a/libLiteRtGpuAccelerator.so) - ไลบรารีแบ็กเอนด์ของ NPU: ไลบรารีที่ใช้ร่วมกันเฉพาะสำหรับการเร่งความเร็วของ NPU
เช่น ไลบรารี QNN HTP ของ Qualcomm (เช่น
@qairt//:lib/aarch64-android/libQnnHtp.so@qairt//:lib/hexagon-v79/unsigned/libQnnHtpV79Skel.so) - ไฟล์และชิ้นงานโมเดล: ไฟล์โมเดลที่ฝึกแล้ว รูปภาพทดสอบ
Shader หรือข้อมูลอื่นๆ ที่จำเป็นในขณะรันไทม์ (เช่น
:model_files:shader_files)
- รันไทม์หลักของ LiteRT: ไลบรารีที่ใช้ร่วมกันของ LiteRT C API หลัก (เช่น
depsแอตทริบิวต์ (Dependency เวลาคอมไพล์): แสดงรายการไลบรารีที่โค้ดของคุณต้องคอมไพล์ด้วย- API และยูทิลิตี LiteRT: ส่วนหัวและไลบรารีแบบคงที่สำหรับคอมโพเนนต์ LiteRT
เช่น บัฟเฟอร์เทนเซอร์ (เช่น
//litert/cc:litert_tensor_buffer) - ไลบรารีกราฟิก (สำหรับ GPU): การขึ้นต่อกันที่เกี่ยวข้องกับกราฟิก API
หากตัวเร่ง GPU ใช้ไลบรารีเหล่านั้น (เช่น
gles_deps())
- API และยูทิลิตี LiteRT: ส่วนหัวและไลบรารีแบบคงที่สำหรับคอมโพเนนต์ LiteRT
เช่น บัฟเฟอร์เทนเซอร์ (เช่น
linkoptsแอตทริบิวต์: ระบุตัวเลือกที่ส่งไปยัง Linker ซึ่งอาจรวมถึงการลิงก์กับไลบรารีของระบบ (เช่น-landroidสำหรับ Android build หรือไลบรารี GLES ที่มีgles_linkopts())
ต่อไปนี้เป็นตัวอย่างของcc_binaryกฎ
cc_binary(
name = "your_application",
srcs = [
"main.cc",
],
data = [
...
# litert c api shared library
"//litert/c:litert_runtime_c_api_shared_lib",
# GPU accelerator shared library
"@litert_gpu//:jni/arm64-v8a/libLiteRtGpuAccelerator.so",
# NPU accelerator shared library
"//litert/vendors/qualcomm/dispatch:dispatch_api_so",
],
linkopts = select({
"@org_tensorflow//tensorflow:android": ["-landroid"],
"//conditions:default": [],
}) + gles_linkopts(), # gles link options
deps = [
...
"//litert/cc:litert_tensor_buffer", # litert cc library
...
] + gles_deps(), # gles dependencies
)
โหลดโมเดล
หลังจากได้รับโมเดล LiteRT หรือแปลงโมเดลเป็นรูปแบบ .tflite
ให้โหลดโมเดลโดยสร้างออบเจ็กต์ Model
LITERT_ASSIGN_OR_RETURN(auto model, Model::CreateFromFile("mymodel.tflite"));
สร้างสภาพแวดล้อม
ออบเจ็กต์ Environment มีสภาพแวดล้อมรันไทม์ที่มีคอมโพเนนต์ต่างๆ
เช่น เส้นทางของปลั๊กอินคอมไพเลอร์และบริบท GPU ต้องระบุ Environment เมื่อสร้าง CompiledModel และ TensorBuffer โค้ดต่อไปนี้
สร้าง Environment สำหรับการดำเนินการ CPU และ GPU โดยไม่มีตัวเลือกใดๆ
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
สร้างโมเดลที่คอมไพล์แล้ว
ใช้ CompiledModel API เพื่อเริ่มต้นรันไทม์ด้วยออบเจ็กต์
Modelที่สร้างขึ้นใหม่ คุณระบุการเร่งฮาร์ดแวร์ได้ในขั้นตอนนี้
(kLiteRtHwAcceleratorCpu หรือ kLiteRtHwAcceleratorGpu)
LITERT_ASSIGN_OR_RETURN(auto compiled_model,
CompiledModel::Create(env, model, kLiteRtHwAcceleratorCpu));
สร้างบัฟเฟอร์อินพุตและเอาต์พุต
สร้างโครงสร้างข้อมูล (บัฟเฟอร์) ที่จำเป็นเพื่อจัดเก็บข้อมูลอินพุตที่คุณจะป้อนลงในโมเดลสำหรับการอนุมาน และข้อมูลเอาต์พุตที่โมเดลสร้างขึ้นหลังจากเรียกใช้การอนุมาน
LITERT_ASSIGN_OR_RETURN(auto input_buffers, compiled_model.CreateInputBuffers());
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());
หากใช้หน่วยความจำ CPU ให้กรอกข้อมูลโดยเขียนข้อมูลลงใน บัฟเฟอร์อินพุตแรกโดยตรง
input_buffers[0].Write<float>(absl::MakeConstSpan(input_data, input_size));
เรียกใช้โมเดล
ระบุบัฟเฟอร์อินพุตและเอาต์พุต จากนั้นเรียกใช้โมเดลที่คอมไพล์แล้วด้วยโมเดล และการเร่งฮาร์ดแวร์ที่ระบุในขั้นตอนก่อนหน้า
compiled_model.Run(input_buffers, output_buffers);
ดึงข้อมูลเอาต์พุต
ดึงข้อมูลผลลัพธ์โดยการอ่านเอาต์พุตของโมเดลจากหน่วยความจำโดยตรง
std::vector<float> data(output_data_size);
output_buffers[0].Read<float>(absl::MakeSpan(data));
// ... process output data
แนวคิดและคอมโพเนนต์หลัก
ดูข้อมูลเกี่ยวกับแนวคิดและคอมโพเนนต์หลักของ API โมเดลที่คอมไพล์แล้วของ LiteRT ได้ในส่วนต่อไปนี้
การจัดการข้อผิดพลาด
LiteRT ใช้ litert::Expected เพื่อแสดงผลค่าหรือส่งต่อข้อผิดพลาดในลักษณะเดียวกับ absl::StatusOr หรือ std::expected คุณตรวจสอบข้อผิดพลาดด้วยตนเองได้
LiteRT มีมาโครต่อไปนี้เพื่อความสะดวก
LITERT_ASSIGN_OR_RETURN(lhs, expr)กำหนดผลลัพธ์ของexprให้กับlhsหาก ไม่ทำให้เกิดข้อผิดพลาด และแสดงผลข้อผิดพลาดในกรณีอื่นๆโดยจะขยายเป็นข้อมูลโค้ดที่มีลักษณะคล้ายกับข้อมูลโค้ดต่อไปนี้
auto maybe_model = Model::CreateFromFile("mymodel.tflite"); if (!maybe_model) { return maybe_model.Error(); } auto model = std::move(maybe_model.Value());LITERT_ASSIGN_OR_ABORT(lhs, expr)ทำหน้าที่เหมือนLITERT_ASSIGN_OR_RETURNแต่จะยกเลิกโปรแกรมในกรณีที่เกิดข้อผิดพลาดLITERT_RETURN_IF_ERROR(expr)จะแสดงผลexprหากการประเมินทำให้เกิดข้อผิดพลาดLITERT_ABORT_IF_ERROR(expr)ทำงานเหมือนกับLITERT_RETURN_IF_ERRORแต่จะ ยกเลิกโปรแกรมในกรณีที่เกิดข้อผิดพลาด
ดูข้อมูลเพิ่มเติมเกี่ยวกับมาโคร LiteRT ได้ที่ litert_macros.h
โมเดลที่คอมไพล์แล้ว (CompiledModel)
Compiled Model API (CompiledModel) มีหน้าที่โหลดโมเดล
ใช้การเร่งฮาร์ดแวร์ สร้างอินสแตนซ์ของรันไทม์ สร้างบัฟเฟอร์อินพุตและ
เอาต์พุต และเรียกใช้การอนุมาน
ข้อมูลโค้ดแบบย่อต่อไปนี้แสดงให้เห็นว่า Compiled Model API
ใช้โมเดล LiteRT (.tflite) และตัวเร่งฮาร์ดแวร์เป้าหมาย (GPU) อย่างไร
และสร้างโมเดลที่คอมไพล์แล้วซึ่งพร้อมที่จะเรียกใช้การอนุมาน
// Load model and initialize runtime
LITERT_ASSIGN_OR_RETURN(auto model, Model::CreateFromFile("mymodel.tflite"));
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
LITERT_ASSIGN_OR_RETURN(auto compiled_model,
CompiledModel::Create(env, model, kLiteRtHwAcceleratorCpu));
ข้อมูลโค้ดแบบย่อต่อไปนี้แสดงให้เห็นว่า Compiled Model API รับบัฟเฟอร์อินพุตและเอาต์พุต และเรียกใช้การอนุมานด้วยโมเดลที่คอมไพล์แล้วอย่างไร
// Preallocate input/output buffers
LITERT_ASSIGN_OR_RETURN(auto input_buffers, compiled_model.CreateInputBuffers());
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());
// Fill the first input
float input_values[] = { /* your data */ };
LITERT_RETURN_IF_ERROR(
input_buffers[0].Write<float>(absl::MakeConstSpan(input_values, /*size*/)));
// Invoke
LITERT_RETURN_IF_ERROR(compiled_model.Run(input_buffers, output_buffers));
// Read the output
std::vector<float> data(output_data_size);
LITERT_RETURN_IF_ERROR(
output_buffers[0].Read<float>(absl::MakeSpan(data)));
ดูมุมมองที่สมบูรณ์ยิ่งขึ้นเกี่ยวกับวิธีติดตั้งใช้งาน CompiledModel API ได้ที่
ซอร์สโค้ดสำหรับ
litert_compiled_model.h
บัฟเฟอร์ Tensor (TensorBuffer)
LiteRT มีการรองรับในตัวสำหรับความสามารถในการทำงานร่วมกันของบัฟเฟอร์ I/O โดยใช้ Tensor Buffer API (TensorBuffer) เพื่อจัดการโฟลว์ของข้อมูลเข้าและออกจากโมเดลที่คอมไพล์แล้ว Tensor Buffer API ช่วยให้เขียน
(Write<T>()) อ่าน (Read<T>()) และล็อกหน่วยความจำ CPU ได้
ดูมุมมองที่สมบูรณ์ยิ่งขึ้นเกี่ยวกับวิธีติดตั้งใช้งาน TensorBuffer API ได้ที่ซอร์สโค้ดของ
litert_tensor_buffer.h
ข้อกำหนดของอินพุต/เอาต์พุตของโมเดลการค้นหา
โดยปกติแล้วข้อกำหนดในการจัดสรรบัฟเฟอร์ Tensor (TensorBuffer) จะ
ระบุโดยฮาร์ดแวร์เร่งความเร็ว บัฟเฟอร์สำหรับอินพุตและเอาต์พุตอาจมี
ข้อกำหนดเกี่ยวกับการจัดแนว สไตรด์ของบัฟเฟอร์ และประเภทหน่วยความจำ คุณสามารถใช้ฟังก์ชันตัวช่วย เช่น CreateInputBuffers เพื่อจัดการข้อกำหนดเหล่านี้โดยอัตโนมัติ
ข้อมูลโค้ดแบบย่อต่อไปนี้แสดงวิธีดึงข้อมูล ข้อกำหนดของบัฟเฟอร์สำหรับข้อมูลอินพุต
LITERT_ASSIGN_OR_RETURN(auto reqs, compiled_model.GetInputBufferRequirements(signature_index, input_index));
ดูมุมมองที่สมบูรณ์ยิ่งขึ้นเกี่ยวกับวิธีใช้ TensorBufferRequirements API ได้ที่ซอร์สโค้ดของ litert_tensor_buffer_requirements.h
สร้างบัฟเฟอร์ Tensor ที่มีการจัดการ (TensorBuffers)
ข้อมูลโค้ดที่ลดความซับซ้อนต่อไปนี้แสดงวิธีสร้าง Managed Tensor
Buffers ซึ่ง TensorBuffer API จะจัดสรรบัฟเฟอร์ที่เกี่ยวข้อง
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_cpu,
TensorBuffer::CreateManaged(env, /*buffer_type=*/kLiteRtTensorBufferTypeHostMemory,
ranked_tensor_type, buffer_size));
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_gl, TensorBuffer::CreateManaged(env,
/*buffer_type=*/kLiteRtTensorBufferTypeGlBuffer, ranked_tensor_type, buffer_size));
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_ahwb, TensorBuffer::CreateManaged(env,
/*buffer_type=*/kLiteRtTensorBufferTypeAhwb, ranked_tensor_type, buffer_size));
สร้างบัฟเฟอร์เทนเซอร์โดยไม่ต้องคัดลอก
หากต้องการห่อหุ้มบัฟเฟอร์ที่มีอยู่เป็น Tensor Buffer (การคัดลอกเป็นศูนย์) ให้ใช้ข้อมูลโค้ดต่อไปนี้
// Create a TensorBuffer from host memory
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_from_host,
TensorBuffer::CreateFromHostMemory(env, ranked_tensor_type,
ptr_to_host_memory, buffer_size));
// Create a TensorBuffer from GlBuffer
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_from_gl,
TensorBuffer::CreateFromGlBuffer(env, ranked_tensor_type, gl_target, gl_id,
size_bytes, offset));
// Create a TensorBuffer from AHardware Buffer
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_from_ahwb,
TensorBuffer::CreateFromAhwb(env, ranked_tensor_type, ahardware_buffer, offset));
การอ่านและการเขียนจากบัฟเฟอร์เทนเซอร์
ข้อมูลโค้ดต่อไปนี้แสดงวิธีอ่านจากบัฟเฟอร์อินพุตและเขียนไปยังบัฟเฟอร์เอาต์พุต
// Example of reading to input buffer:
std::vector<float> input_tensor_data = {1,2};
LITERT_ASSIGN_OR_RETURN(auto write_success,
input_tensor_buffer.Write<float>(absl::MakeConstSpan(input_tensor_data)));
if(write_success){
/* Continue after successful write... */
}
// Example of writing to output buffer:
std::vector<float> data(total_elements);
LITERT_ASSIGN_OR_RETURN(auto read_success,
output_tensor_buffer.Read<float>(absl::MakeSpan(data)));
if(read_success){
/* Continue after successful read */
}
ขั้นสูง: การทำงานร่วมกันของบัฟเฟอร์แบบไม่คัดลอกสำหรับบัฟเฟอร์ฮาร์ดแวร์ประเภทเฉพาะ
บัฟเฟอร์บางประเภท เช่น AHardwareBuffer อนุญาตให้ทำงานร่วมกับบัฟเฟอร์ประเภทอื่นๆ ได้
ตัวอย่างเช่น คุณสามารถสร้างบัฟเฟอร์ OpenGL จาก
AHardwareBuffer โดยใช้การคัดลอกแบบไม่มีการคัดลอก ข้อมูลโค้ดต่อไปนี้แสดงตัวอย่าง
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_ahwb,
TensorBuffer::CreateManaged(env, kLiteRtTensorBufferTypeAhwb,
ranked_tensor_type, buffer_size));
// Buffer interop: Get OpenGL buffer from AHWB,
// internally creating an OpenGL buffer backed by AHWB memory.
LITERT_ASSIGN_OR_RETURN(auto gl_buffer, tensor_buffer_ahwb.GetGlBuffer());
นอกจากนี้ คุณยังสร้างบัฟเฟอร์ OpenCL จาก AHardwareBuffer ได้ด้วย
LITERT_ASSIGN_OR_RETURN(auto cl_buffer, tensor_buffer_ahwb.GetOpenClMemory());
ในอุปกรณ์เคลื่อนที่ที่รองรับการทำงานร่วมกันระหว่าง OpenCL กับ OpenGL คุณจะสร้างบัฟเฟอร์ CL จากบัฟเฟอร์ GL ได้โดยทำดังนี้
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_from_gl,
TensorBuffer::CreateFromGlBuffer(env, ranked_tensor_type, gl_target, gl_id,
size_bytes, offset));
// Creates an OpenCL buffer from the OpenGL buffer, zero-copy.
LITERT_ASSIGN_OR_RETURN(auto cl_buffer, tensor_buffer_from_gl.GetOpenClMemory());
ตัวอย่างการติดตั้งใช้งาน
ดูการติดตั้งใช้งาน LiteRT ใน C++ ต่อไปนี้
การอนุมานพื้นฐาน (CPU)
ต่อไปนี้คือข้อมูลโค้ดเวอร์ชันย่อจากส่วนเริ่มต้นใช้งาน ซึ่งเป็นการใช้งานการอนุมานที่ง่ายที่สุด ด้วย LiteRT
// Load model and initialize runtime
LITERT_ASSIGN_OR_RETURN(auto model, Model::CreateFromFile("mymodel.tflite"));
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
LITERT_ASSIGN_OR_RETURN(auto compiled_model, CompiledModel::Create(env, model,
kLiteRtHwAcceleratorCpu));
// Preallocate input/output buffers
LITERT_ASSIGN_OR_RETURN(auto input_buffers, compiled_model.CreateInputBuffers());
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());
// Fill the first input
float input_values[] = { /* your data */ };
input_buffers[0].Write<float>(absl::MakeConstSpan(input_values, /*size*/));
// Invoke
compiled_model.Run(input_buffers, output_buffers);
// Read the output
std::vector<float> data(output_data_size);
output_buffers[0].Read<float>(absl::MakeSpan(data));
การคัดลอกเป็นศูนย์ด้วยหน่วยความจำของโฮสต์
LiteRT Compiled Model API ช่วยลดอุปสรรคของไปป์ไลน์การอนุมาน
โดยเฉพาะเมื่อต้องจัดการกับแบ็กเอนด์ฮาร์ดแวร์หลายรายการและโฟลว์แบบไม่คัดลอก ข้อมูลโค้ดต่อไปนี้ใช้เมธอด CreateFromHostMemory เมื่อสร้างบัฟเฟอร์อินพุต ซึ่งใช้การคัดลอกเป็นศูนย์กับหน่วยความจำของโฮสต์
// Define an LiteRT environment to use existing EGL display and context.
const std::vector<Environment::Option> environment_options = {
{OptionTag::EglDisplay, user_egl_display},
{OptionTag::EglContext, user_egl_context}};
LITERT_ASSIGN_OR_RETURN(auto env,
Environment::Create(absl::MakeConstSpan(environment_options)));
// Load model1 and initialize runtime.
LITERT_ASSIGN_OR_RETURN(auto model1, Model::CreateFromFile("model1.tflite"));
LITERT_ASSIGN_OR_RETURN(auto compiled_model1, CompiledModel::Create(env, model1, kLiteRtHwAcceleratorGpu));
// Prepare I/O buffers. opengl_buffer is given outside from the producer.
LITERT_ASSIGN_OR_RETURN(auto tensor_type, model.GetInputTensorType("input_name0"));
// Create an input TensorBuffer based on tensor_type that wraps the given OpenGL Buffer.
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_from_opengl,
litert::TensorBuffer::CreateFromGlBuffer(env, tensor_type, opengl_buffer));
// Create an input event and attach it to the input buffer. Internally, it creates
// and inserts a fence sync object into the current EGL command queue.
LITERT_ASSIGN_OR_RETURN(auto input_event, Event::CreateManaged(env, LiteRtEventTypeEglSyncFence));
tensor_buffer_from_opengl.SetEvent(std::move(input_event));
std::vector<TensorBuffer> input_buffers;
input_buffers.push_back(std::move(tensor_buffer_from_opengl));
// Create an output TensorBuffer of the model1. It's also used as an input of the model2.
LITERT_ASSIGN_OR_RETURN(auto intermedidate_buffers, compiled_model1.CreateOutputBuffers());
// Load model2 and initialize runtime.
LITERT_ASSIGN_OR_RETURN(auto model2, Model::CreateFromFile("model2.tflite"));
LITERT_ASSIGN_OR_RETURN(auto compiled_model2, CompiledModel::Create(env, model2, kLiteRtHwAcceleratorGpu));
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model2.CreateOutputBuffers());
compiled_model1.RunAsync(input_buffers, intermedidate_buffers);
compiled_model2.RunAsync(intermedidate_buffers, output_buffers);