LiteRT Next API พร้อมใช้งานใน C++ และช่วยให้นักพัฒนาแอป Android ควบคุมการจัดสรรหน่วยความจำและการพัฒนาระดับล่างได้มากกว่า Kotlin API
ดูตัวอย่างแอปพลิเคชัน LiteRT Next ใน C++ ได้ที่การแบ่งกลุ่มแบบไม่ต่อเนื่องด้วย C++ เดโม
เริ่มต้นใช้งาน
ทําตามขั้นตอนต่อไปนี้เพื่อเพิ่ม LiteRT Next ลงในแอปพลิเคชัน 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
(Dependency รันไทม์): แอตทริบิวต์นี้สำคัญอย่างยิ่งสำหรับการจัดแพ็กเกจไฟล์ที่มีการใช้งานร่วมกันและชิ้นงานที่แอปพลิเคชันโหลดเมื่อรันไทม์- LiteRT Core Runtime: ไลบรารีที่ใช้ร่วมกันหลักของ LiteRT C API (เช่น
//litert/c:litert_runtime_c_api_shared_lib
) - ไลบรารี Dispatch: ไลบรารีที่ใช้ร่วมกันเฉพาะของผู้ให้บริการที่ 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
) - ไฟล์และชิ้นงานโมเดล: ไฟล์โมเดลที่ผ่านการฝึกอบรม รูปภาพทดสอบ ชิดเดอร์ หรือข้อมูลอื่นๆ ที่จำเป็นในรันไทม์ (เช่น
:model_files
,:shader_files
)
- LiteRT Core Runtime: ไลบรารีที่ใช้ร่วมกันหลักของ LiteRT C API (เช่น
- แอตทริบิวต์
deps
(Dependency Compile-time): แสดงรายการไลบรารีที่โค้ดของคุณต้องคอมไพล์- LiteRT API และยูทิลิตี: ส่วนหัวและไลบรารีแบบคงที่สำหรับคอมโพเนนต์ LiteRT เช่น บัฟเฟอร์เทนเซอร์ (เช่น
//litert/cc:litert_tensor_buffer
) - ไลบรารีกราฟิก (สำหรับ GPU): ไลบรารีที่เชื่อมโยงกับ API กราฟิกหากตัวเร่ง GPU ใช้ไลบรารีดังกล่าว (เช่น
gles_deps()
)
- LiteRT API และยูทิลิตี: ส่วนหัวและไลบรารีแบบคงที่สำหรับคอมโพเนนต์ LiteRT เช่น บัฟเฟอร์เทนเซอร์ (เช่น
linkopts
แอตทริบิวต์: ระบุตัวเลือกที่ส่งไปยังตัวลิงก์ ซึ่งอาจรวมถึงการลิงก์กับไลบรารีของระบบ (เช่น-landroid
สำหรับบิลด์ Android หรือไลบรารี 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
แนวคิดและคอมโพเนนต์หลัก
ดูข้อมูลเกี่ยวกับแนวคิดและคอมโพเนนต์หลักของ LiteRT Next API ได้ที่ส่วนต่อไปนี้
การจัดการข้อผิดพลาด
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 Next รองรับการทำงานร่วมกันของบัฟเฟอร์ I/O ในตัวโดยใช้ Tensor Buffer API (TensorBuffer
) เพื่อจัดการการไหลเข้าและออกจากโมเดลที่คอมไพล์ Tensor Buffer API ช่วยให้คุณเขียน (Write<T>()
) และอ่าน (Read<T>()
) รวมถึงล็อกหน่วยความจำของ CPU ได้
ดูมุมมองที่สมบูรณ์ยิ่งขึ้นเกี่ยวกับวิธีติดตั้งใช้งาน TensorBuffer
API ได้ที่ซอร์สโค้ดของ litert_tensor_buffer.h
ข้อกําหนดอินพุต/เอาต์พุตของโมเดลการค้นหา
โดยปกติแล้วข้อกำหนดในการจอง Tensor Buffer (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 ด้วยการทำงานแบบไม่คัดลอก
หากต้องการตัดบัฟเฟอร์ที่มีอยู่เป็นบัฟเฟอร์ Tensor (การคัดลอกข้อมูลแบบไม่ใช้พื้นที่เก็บข้อมูลเพิ่ม) ให้ใช้ข้อมูลโค้ดต่อไปนี้
// 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));
การอ่านและเขียนจากบัฟเฟอร์ Tensor
ข้อมูลโค้ดต่อไปนี้แสดงวิธีอ่านจากบัฟเฟอร์อินพุตและเขียนไปยังบัฟเฟอร์เอาต์พุต
// 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 Next ใน C++ ต่อไปนี้
การทำนายพื้นฐาน (CPU)
ต่อไปนี้คือข้อมูลโค้ดฉบับย่อจากส่วนเริ่มต้นใช้งาน ซึ่งเป็นการใช้งานการอนุมานที่ง่ายที่สุดด้วย LiteRT Next
// 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 Next 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);