LiteRT Next API tersedia di C++, dan dapat menawarkan kontrol yang lebih besar kepada developer Android terhadap alokasi memori dan pengembangan tingkat rendah dibandingkan dengan Kotlin API.
Untuk contoh aplikasi LiteRT Next di C++, lihat Demo segmentasi asinkron dengan C++.
Mulai
Gunakan langkah-langkah berikut untuk menambahkan LiteRT Next ke aplikasi Android Anda.
Memperbarui konfigurasi build
Mem-build aplikasi C++ dengan LiteRT untuk akselerasi GPU, NPU, dan CPU menggunakan
Bazel melibatkan penentuan aturan cc_binary
untuk memastikan semua
komponen yang diperlukan dikompilasi, ditautkan, dan dipaketkan. Contoh
penyiapan berikut memungkinkan aplikasi Anda memilih atau menggunakan akselerator GPU, NPU, dan
CPU secara dinamis.
Berikut adalah komponen utama dalam konfigurasi build Bazel Anda:
- Aturan
cc_binary
: Ini adalah aturan Bazel dasar yang digunakan untuk menentukan target yang dapat dieksekusi C++ (misalnya,name = "your_application_name"
). - Atribut
srcs
: Mencantumkan file sumber C++ aplikasi Anda (misalnya,main.cc
, dan file.cc
atau.h
lainnya). - Atribut
data
(Dependensi Runtime): Atribut ini sangat penting untuk memaketkan library bersama dan aset yang dimuat aplikasi Anda saat runtime.- LiteRT Core Runtime: Library bersama LiteRT C API utama (misalnya,
//litert/c:litert_runtime_c_api_shared_lib
). - Library Pengiriman: Library bersama khusus vendor yang digunakan LiteRT
untuk berkomunikasi dengan driver hardware (misalnya,
//litert/vendors/qualcomm/dispatch:dispatch_api_so
). - Library Backend GPU: Library bersama untuk akselerasi GPU
(misalnya,
"@litert_gpu//:jni/arm64-v8a/libLiteRtGpuAccelerator.so
). - Library Backend NPU: Library bersama khusus untuk akselerasi
NPU, seperti library QNN HTP Qualcomm (misalnya,
@qairt//:lib/aarch64-android/libQnnHtp.so
,@qairt//:lib/hexagon-v79/unsigned/libQnnHtpV79Skel.so
). - File & Aset Model: File model terlatih, gambar pengujian, shader, atau data lain yang diperlukan saat runtime (misalnya,
:model_files
,:shader_files
).
- LiteRT Core Runtime: Library bersama LiteRT C API utama (misalnya,
- Atribut
deps
(Dependensi Waktu Kompilasi): Atribut ini mencantumkan library yang harus dikompilasi oleh kode Anda.- LiteRT API & Utilitas: Header dan library statis untuk komponen LiteRT
seperti buffering tensor (misalnya,
//litert/cc:litert_tensor_buffer
). - Library Grafis (untuk GPU): Dependensi yang terkait dengan API grafis
jika akselerator GPU menggunakannya (misalnya,
gles_deps()
).
- LiteRT API & Utilitas: Header dan library statis untuk komponen LiteRT
seperti buffering tensor (misalnya,
- Atribut
linkopts
: Menentukan opsi yang diteruskan ke penaut, yang dapat menyertakan penautan ke library sistem (mis.,-landroid
untuk build Android, atau library GLES dengangles_linkopts()
).
Berikut adalah contoh aturan 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
)
Memuat Model
Setelah mendapatkan model LiteRT, atau mengonversi model ke format .tflite
,
muat model dengan membuat objek Model
.
LITERT_ASSIGN_OR_RETURN(auto model, Model::CreateFromFile("mymodel.tflite"));
Membuat lingkungan
Objek Environment
menyediakan lingkungan runtime yang menyertakan komponen
seperti jalur plugin compiler dan konteks GPU. Environment
diperlukan saat membuat CompiledModel
dan TensorBuffer
. Kode berikut
membuat Environment
untuk eksekusi CPU dan GPU tanpa opsi apa pun:
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
Membuat Model yang Dikompilasi
Dengan menggunakan CompiledModel
API, lakukan inisialisasi runtime dengan objek Model
yang baru dibuat. Anda dapat menentukan akselerasi hardware pada tahap ini
(kLiteRtHwAcceleratorCpu
atau kLiteRtHwAcceleratorGpu
):
LITERT_ASSIGN_OR_RETURN(auto compiled_model,
CompiledModel::Create(env, model, kLiteRtHwAcceleratorCpu));
Membuat Buffer Input dan Output
Buat struktur data (buffer) yang diperlukan untuk menyimpan data input yang akan Anda masukkan ke dalam model untuk inferensi, dan data output yang dihasilkan model setelah menjalankan inferensi.
LITERT_ASSIGN_OR_RETURN(auto input_buffers, compiled_model.CreateInputBuffers());
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());
Jika Anda menggunakan memori CPU, isi input dengan menulis data langsung ke buffer input pertama.
input_buffers[0].Write<float>(absl::MakeConstSpan(input_data, input_size));
Memanggil model
Dengan menyediakan buffering input dan output, jalankan Model yang Dikompilasi dengan model dan akselerasi hardware yang ditentukan pada langkah sebelumnya.
compiled_model.Run(input_buffers, output_buffers);
Mengambil Output
Ambil output dengan membaca output model secara langsung dari memori.
std::vector<float> data(output_data_size);
output_buffers[0].Read<float>(absl::MakeSpan(data));
// ... process output data
Konsep dan komponen utama
Lihat bagian berikut untuk mengetahui informasi tentang konsep dan komponen utama LiteRT Next API.
Penanganan Error
LiteRT menggunakan litert::Expected
untuk menampilkan nilai atau menyebarkan error dengan cara yang serupa dengan absl::StatusOr
atau std::expected
. Anda dapat memeriksa error
secara manual.
Untuk memudahkan, LiteRT menyediakan makro berikut:
LITERT_ASSIGN_OR_RETURN(lhs, expr)
menetapkan hasilexpr
kelhs
jika tidak menghasilkan error, dan menampilkan error jika tidak.Tindakan ini akan memperluasnya menjadi seperti cuplikan berikut.
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)
melakukan hal yang sama sepertiLITERT_ASSIGN_OR_RETURN
, tetapi membatalkan program jika terjadi error.LITERT_RETURN_IF_ERROR(expr)
menampilkanexpr
jika evaluasinya menghasilkan error.LITERT_ABORT_IF_ERROR(expr)
melakukan hal yang sama sepertiLITERT_RETURN_IF_ERROR
, tetapi menghentikan program jika terjadi error.
Untuk informasi selengkapnya tentang makro LiteRT, lihat litert_macros.h
.
Model yang Dikompilasi (CompiledModel)
Compiled Model API (CompiledModel
) bertanggung jawab untuk memuat model,
menerapkan akselerasi hardware, membuat instance runtime, membuat buffering input dan
output, serta menjalankan inferensi.
Cuplikan kode sederhana berikut menunjukkan cara Compiled Model API
mengambil model LiteRT (.tflite
) dan akselerator hardware target (GPU), serta
membuat model kompilasi yang siap menjalankan inferensi.
// 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));
Cuplikan kode sederhana berikut menunjukkan cara Compiled Model API mengambil buffering input dan output, serta menjalankan inferensi dengan model yang dikompilasi.
// 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)));
Untuk melihat cara penerapan CompiledModel
API secara lebih lengkap, lihat
kode sumber untuk
litert_compiled_model.h.
Buffer Tensor (TensorBuffer)
LiteRT Next menyediakan dukungan bawaan untuk interoperabilitas buffering I/O, menggunakan
Tensor Buffer API (TensorBuffer
) untuk menangani aliran data masuk dan keluar dari
model yang dikompilasi. Tensor Buffer API menyediakan kemampuan untuk menulis
(Write<T>()
) dan membaca (Read<T>()
), serta mengunci memori CPU.
Untuk melihat cara penerapan TensorBuffer
API secara lebih lengkap, lihat
kode sumber untuk
litert_tensor_buffer.h.
Persyaratan input/output model kueri
Persyaratan untuk mengalokasikan Buffer Tensor (TensorBuffer
) biasanya
ditentukan oleh akselerator hardware. Buffer untuk input dan output dapat memiliki
persyaratan terkait perataan, langkah buffer, dan jenis memori. Anda dapat menggunakan
fungsi bantuan seperti CreateInputBuffers
untuk menangani persyaratan
ini secara otomatis.
Cuplikan kode sederhana berikut menunjukkan cara mengambil persyaratan buffering untuk data input:
LITERT_ASSIGN_OR_RETURN(auto reqs, compiled_model.GetInputBufferRequirements(signature_index, input_index));
Untuk melihat cara penerapan TensorBufferRequirements
API
secara lebih lengkap, lihat kode sumber untuk
litert_tensor_buffer_requirements.h.
Membuat Buffer Tensor Terkelola (TensorBuffers)
Cuplikan kode sederhana berikut menunjukkan cara membuat Buffer Matriks
Terkelola, tempat TensorBuffer
API mengalokasikan buffer masing-masing:
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));
Membuat Buffer Tensor dengan zero-copy
Untuk menggabungkan buffer yang ada sebagai Buffer Tensor (zero-copy), gunakan cuplikan kode berikut:
// 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));
Membaca dan menulis dari Buffer Tensor
Cuplikan berikut menunjukkan cara membaca dari buffering input dan menulis ke buffering output:
// 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 */
}
Lanjutan: Interop buffer zero-copy untuk jenis buffer hardware khusus
Jenis buffering tertentu, seperti AHardwareBuffer
, memungkinkan interoperabilitas dengan
jenis buffering lainnya. Misalnya, buffer OpenGL dapat dibuat dari
AHardwareBuffer
dengan zero-copy. Cuplikan kode berikut menunjukkan contoh:
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());
Buffer OpenCL juga dapat dibuat dari AHardwareBuffer
:
LITERT_ASSIGN_OR_RETURN(auto cl_buffer, tensor_buffer_ahwb.GetOpenClMemory());
Pada perangkat seluler yang mendukung interoperabilitas antara OpenCL dan OpenGL, buffer CL dapat dibuat dari buffer 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());
Contoh penerapan
Lihat implementasi LiteRT Next berikut di C++.
Inferensi Dasar (CPU)
Berikut adalah versi ringkas cuplikan kode dari bagian Mulai Mulai. Ini adalah implementasi inferensi paling sederhana dengan 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));
Zero-Copy dengan Memori Host
LiteRT Next Compiled Model API mengurangi hambatan pipeline inferensi,
terutama saat menangani beberapa backend hardware dan alur zero-copy. Cuplikan
kode berikut menggunakan metode CreateFromHostMemory
saat membuat
buffer input, yang menggunakan zero-copy dengan memori host.
// 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);