LiteRT Next API, C++ में उपलब्ध हैं. ये Android डेवलपर को Kotlin एपीआई के मुकाबले, मेमोरी के ऐलोकेशन और लो-लेवल डेवलपमेंट पर ज़्यादा कंट्रोल दे सकते हैं.
C++ में LiteRT Next ऐप्लिकेशन का उदाहरण देखने के लिए, C++ के साथ असाइनमेंट के हिसाब से डेटा बांटने की सुविधा का डेमो देखें.
शुरू करें
अपने Android ऐप्लिकेशन में LiteRT Next जोड़ने के लिए, यह तरीका अपनाएं.
बिल्ड कॉन्फ़िगरेशन अपडेट करना
Bazel का इस्तेमाल करके, जीपीयू, एनपीयू, और सीपीयू को तेज़ करने के लिए, LiteRT के साथ C++ ऐप्लिकेशन बनाने के लिए, cc_binary
नियम तय करना ज़रूरी है. इससे यह पक्का होता है कि सभी ज़रूरी कॉम्पोनेंट को कंपाइल, लिंक, और पैकेज किया गया है. यहां दिए गए उदाहरण के सेटअप की मदद से, आपके ऐप्लिकेशन को जीपीयू, एनपीयू, और सीपीयू ऐक्सेलरेटर को डाइनैमिक तरीके से चुनने या इस्तेमाल करने की अनुमति मिलती है.
Bazel के बिल्ड कॉन्फ़िगरेशन के मुख्य कॉम्पोनेंट यहां दिए गए हैं:
cc_binary
नियम: यह Bazel का बुनियादी नियम है.इसका इस्तेमाल, C++ के टारगेट को तय करने के लिए किया जाता है. जैसे,name = "your_application_name"
).srcs
एट्रिब्यूट: इसमें आपके ऐप्लिकेशन की C++ सोर्स फ़ाइलों की सूची होती है (उदाहरण के लिए,main.cc
और अन्य.cc
या.h
फ़ाइलें).data
एट्रिब्यूट (रनटाइम डिपेंडेंसी): यह उन शेयर की गई लाइब्रेरी और एसेट को पैकेज करने के लिए ज़रूरी है जिन्हें आपका ऐप्लिकेशन रनटाइम के दौरान लोड करता है.- LiteRT Core Runtime: LiteRT C API की मुख्य शेयर की गई लाइब्रेरी (उदाहरण के लिए,
//litert/c:litert_runtime_c_api_shared_lib
). - डिस्पैच लाइब्रेरी: वेंडर के हिसाब से शेयर की गई लाइब्रेरी, जिनका इस्तेमाल LiteRT, हार्डवेयर ड्राइवर के साथ कम्यूनिकेट करने के लिए करता है. उदाहरण के लिए,
//litert/vendors/qualcomm/dispatch:dispatch_api_so
). - जीपीयू बैकएंड लाइब्रेरी: जीपीयू की मदद से प्रोसेसिंग की स्पीड बढ़ाने के लिए शेयर की गई लाइब्रेरी (उदाहरण के लिए,
"@litert_gpu//:jni/arm64-v8a/libLiteRtGpuAccelerator.so
). - एनपीयू बैकएंड लाइब्रेरी: एनपीयू के तेज़ी से काम करने के लिए, शेयर की गई खास लाइब्रेरी. जैसे, Qualcomm की QNN HTP लाइब्रेरी (उदाहरण के लिए,
@qairt//:lib/aarch64-android/libQnnHtp.so
,@qairt//:lib/hexagon-v79/unsigned/libQnnHtpV79Skel.so
). - मॉडल फ़ाइलें और ऐसेट: ट्रेन की गई मॉडल फ़ाइलें, जांच के लिए इस्तेमाल की जाने वाली इमेज,
शेडर या रनटाइम के दौरान ज़रूरी कोई अन्य डेटा (उदाहरण के लिए,
:model_files
,:shader_files
).
- LiteRT Core Runtime: LiteRT C API की मुख्य शेयर की गई लाइब्रेरी (उदाहरण के लिए,
deps
एट्रिब्यूट (कंपाइल के समय की डिपेंडेंसी): इसमें उन लाइब्रेरी की सूची होती है जिनके साथ आपके कोड को कंपाइल करना ज़रूरी है.- LiteRT एपीआई और यूटिलिटी: टेंसर बफ़र (उदाहरण के लिए,
//litert/cc:litert_tensor_buffer
). - ग्राफ़िक्स लाइब्रेरी (जीपीयू के लिए): ग्राफ़िक्स एपीआई से जुड़ी डिपेंडेंसी, अगर जीपीयू ऐक्सेलरेटर उनका इस्तेमाल करता है (उदाहरण के लिए,
gles_deps()
).
- LiteRT एपीआई और यूटिलिटी: टेंसर बफ़र (उदाहरण के लिए,
linkopts
एट्रिब्यूट: इससे लिंकर को दिए गए विकल्पों के बारे में पता चलता है. इनमें सिस्टम लाइब्रेरी (उदाहरण के लिए,-landroid
के लिए Android बिल्ड याgles_linkopts()
के साथ GLES लाइब्रेरी).
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
ऑब्जेक्ट, रनटाइम एनवायरमेंट उपलब्ध कराता है. इसमें कॉम्पाइलर प्लग इन और जीपीयू कॉन्टेक्स्ट का पाथ जैसे कॉम्पोनेंट शामिल होते हैं. CompiledModel
और TensorBuffer
बनाते समय, Environment
देना ज़रूरी है. यहां दिया गया कोड, सीपीयू और जीपीयू के लिए, बिना किसी विकल्प के Environment
बनाता है:
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
कंपाइल किया गया मॉडल बनाना
CompiledModel
एपीआई का इस्तेमाल करके, नए बनाए गए 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());
अगर सीपीयू मेमोरी का इस्तेमाल किया जा रहा है, तो सीधे पहले इनपुट बफ़र में डेटा लिखकर इनपुट भरें.
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
को असाइन करता है. ऐसा तब होता है, जबexpr
से कोई गड़बड़ी नहीं होती. अगर गड़बड़ी होती है, तोLITERT_ASSIGN_OR_RETURN(lhs, expr)
गड़बड़ी दिखाता है.यह इस स्निपेट की तरह बड़ा हो जाएगा.
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
) और टारगेट हार्डवेयर ऐक्सेलरेटर (जीपीयू) को कैसे लेता है और एक ऐसा कंपाइल किया गया मॉडल बनाता है जो अनुमान लगाने के लिए तैयार है.
// 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
एपीआई को लागू करने के तरीके के बारे में ज़्यादा जानने के लिए, litert_compiled_model.h का सोर्स कोड देखें.
टेंसर बफ़र (TensorBuffer)
LiteRT Next में, I/O बफ़र के साथ काम करने की सुविधा पहले से मौजूद है. इसके लिए, यह संकलित मॉडल में और उससे बाहर डेटा के फ़्लो को मैनेज करने के लिए, टेन्सर बफ़र एपीआई (TensorBuffer
) का इस्तेमाल करता है. Tensor Buffer API, लिखने (Write<T>()
) और पढ़ने (Read<T>()
) के साथ-साथ सीपीयू मेमोरी को लॉक करने की सुविधा देता है.
TensorBuffer
एपीआई को लागू करने के तरीके के बारे में ज़्यादा जानने के लिए, litert_tensor_buffer.h का सोर्स कोड देखें.
क्वेरी मॉडल के इनपुट/आउटपुट की ज़रूरी शर्तें
आम तौर पर, हार्डवेयर ऐक्सेलरेटर से यह तय होता है कि Tensor Buffer (TensorBuffer
) को कैसे असाइन किया जाए. इनपुट और आउटपुट के लिए बफ़र में, अलाइनमेंट, बफ़र स्ट्राइड, और मेमोरी टाइप से जुड़ी ज़रूरी शर्तें हो सकती हैं. इन ज़रूरी शर्तों को अपने-आप मैनेज करने के लिए, CreateInputBuffers
जैसे हेल्पर फ़ंक्शन का इस्तेमाल किया जा सकता है.
यहां दिए गए आसान कोड स्निपेट में, इनपुट डेटा के लिए बफ़र की ज़रूरी शर्तें वापस पाने का तरीका बताया गया है:
LITERT_ASSIGN_OR_RETURN(auto reqs, compiled_model.GetInputBufferRequirements(signature_index, input_index));
TensorBufferRequirements
एपीआई को लागू करने के तरीके के बारे में ज़्यादा जानने के लिए, litert_tensor_buffer_requirements.h का सोर्स कोड देखें.
मैनेज किए जा सकने वाले टेंसर बफ़र (TensorBuffers) बनाना
यहां दिए गए आसान कोड स्निपेट में, मैनेज किए जा रहे टेंसर बफ़र बनाने का तरीका बताया गया है. इसमें TensorBuffer
एपीआई, संबंधित बफ़र को असाइन करता है:
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));
बिना कॉपी किए टेंसर बफ़र बनाना
किसी मौजूदा बफ़र को टेंसर बफ़र (ज़ीरो-कॉपी) के तौर पर रैप करने के लिए, नीचे दिए गए कोड स्निपेट का इस्तेमाल करें:
// 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
जैसे कुछ बफ़र टाइप, दूसरे बफ़र टाइप के साथ काम करने की अनुमति देते हैं. उदाहरण के लिए, ज़ीरो-कॉपी वाले AHardwareBuffer
से OpenGL बफ़र बनाया जा सकता है. यहां दिया गया कोड-स्निपेट एक उदाहरण दिखाता है:
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 के बीच इंटरऑपरेबिलिटी की सुविधा वाले मोबाइल डिवाइसों पर, GL बफ़र से CL बफ़र बनाए जा सकते हैं:
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());
लागू करने का उदाहरण
C++ में LiteRT Next को लागू करने के तरीके के बारे में यहां बताया गया है.
बुनियादी अनुमान (सीपीयू)
यहां शुरू करें सेक्शन के कोड स्निपेट का छोटा वर्शन दिया गया है. 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 कंपाइल किया गया मॉडल एपीआई, अनुमान लगाने वाली पाइपलाइन के काम करने में आने वाली रुकावटों को कम करता है. ऐसा खास तौर पर, कई हार्डवेयर बैकएंड और शून्य कॉपी फ़्लो के साथ काम करते समय होता है. यहां दिया गया कोड स्निपेट, इनपुट बफ़र बनाते समय 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);