LiteRT নেক্সট এপিআইগুলি C++ এ উপলব্ধ, এবং কোটলিন এপিআইগুলির তুলনায় অ্যান্ড্রয়েড বিকাশকারীদের মেমরি বরাদ্দকরণ এবং নিম্ন-স্তরের বিকাশের উপর আরও বেশি নিয়ন্ত্রণ অফার করতে পারে।
C++ এ LiteRT Next অ্যাপ্লিকেশনের উদাহরণের জন্য, C++ ডেমোর সাথে অ্যাসিঙ্ক্রোনাস সেগমেন্টেশন দেখুন।
শুরু করুন
আপনার অ্যান্ড্রয়েড অ্যাপ্লিকেশনের পাশে LiteRT যুক্ত করতে নিম্নলিখিত পদক্ষেপগুলি ব্যবহার করুন৷
বিল্ড কনফিগারেশন আপডেট করুন
Bazel ব্যবহার করে GPU, NPU এবং CPU ত্বরণের জন্য LiteRT-এর সাহায্যে একটি C++ অ্যাপ্লিকেশন তৈরি করার জন্য একটি cc_binary
নিয়ম সংজ্ঞায়িত করা হয় যাতে সমস্ত প্রয়োজনীয় উপাদানগুলি সংকলিত, লিঙ্ক করা এবং প্যাকেজ করা হয়। নিম্নলিখিত উদাহরণ সেটআপটি আপনার অ্যাপ্লিকেশনটিকে গতিশীলভাবে GPU, NPU এবং CPU অ্যাক্সিলারেটরগুলি বেছে নিতে বা ব্যবহার করতে দেয়৷
আপনার বেজেল বিল্ড কনফিগারেশনের মূল উপাদানগুলি এখানে রয়েছে:
-
cc_binary
নিয়ম: এটি আপনার 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 ত্বরণের জন্য নির্দিষ্ট শেয়ার করা লাইব্রেরি, যেমন Qualcomm-এর QNN HTP লাইব্রেরি (যেমন,
@qairt//:lib/aarch64-android/libQnnHtp.so
,@qairt//:lib/hexagon-v79/unsigned/libQnnHtpV79Skel.so
. - মডেল ফাইল এবং সম্পদ: আপনার প্রশিক্ষিত মডেল ফাইল, পরীক্ষার ছবি, শেডার, বা রানটাইমে প্রয়োজনীয় অন্য কোনো ডেটা (যেমন,
:model_files
,:shader_files
)।
- LiteRT কোর রানটাইম: প্রধান LiteRT C API ভাগ করা লাইব্রেরি (যেমন,
-
deps
অ্যাট্রিবিউট (কম্পাইল-টাইম নির্ভরতা): এটি আপনার কোডের সাথে কম্পাইল করার জন্য প্রয়োজনীয় লাইব্রেরির তালিকা করে।- LiteRT API এবং ইউটিলিটি: টেনসর বাফারের মতো LiteRT উপাদানগুলির জন্য হেডার এবং স্ট্যাটিক লাইব্রেরি (যেমন,
//litert/cc:litert_tensor_buffer
)। - গ্রাফিক্স লাইব্রেরি (GPU-এর জন্য): GPU অ্যাক্সিলারেটর ব্যবহার করলে গ্রাফিক্স API-এর সাথে সম্পর্কিত নির্ভরতা (যেমন,
gles_deps()
)।
- LiteRT API এবং ইউটিলিটি: টেনসর বাফারের মতো LiteRT উপাদানগুলির জন্য হেডার এবং স্ট্যাটিক লাইব্রেরি (যেমন,
-
linkopts
অ্যাট্রিবিউট: লিঙ্কারকে পাস করা বিকল্পগুলি নির্দিষ্ট করে, যার মধ্যে সিস্টেম লাইব্রেরির বিরুদ্ধে লিঙ্ক করা অন্তর্ভুক্ত থাকতে পারে (যেমন, Android বিল্ডের জন্য-landroid
, অথবা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
প্রয়োজন। নিম্নলিখিত কোড কোন বিকল্প ছাড়াই CPU এবং GPU নির্বাহের জন্য একটি Environment
তৈরি করে:
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)
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
) একটি মডেল লোড করা, হার্ডওয়্যার ত্বরণ প্রয়োগ, রানটাইম ইনস্ট্যান্ট করা, ইনপুট এবং আউটপুট বাফার তৈরি করা এবং অনুমান চালানোর জন্য দায়ী।
নিম্নলিখিত সরলীকৃত কোড স্নিপেট দেখায় কিভাবে কম্পাইল করা মডেল এপিআই একটি 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));
নিম্নলিখিত সরলীকৃত কোড স্নিপেট দেখায় কিভাবে কম্পাইল করা মডেল এপিআই একটি ইনপুট এবং আউটপুট বাফার নেয় এবং কম্পাইল করা মডেলের সাথে অনুমান চালায়।
// 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 কীভাবে প্রয়োগ করা হয় তার আরও সম্পূর্ণ দর্শনের জন্য, litter_compiled_model.h এর সোর্স কোডটি দেখুন।
টেনসর বাফার (টেনসরবাফার)
LiteRT Next I/O বাফার ইন্টারঅপারেবিলিটির জন্য অন্তর্নির্মিত সমর্থন প্রদান করে, Tensor Buffer API ( TensorBuffer
) ব্যবহার করে সংকলিত মডেলের মধ্যে এবং বাইরে ডেটা প্রবাহ পরিচালনা করে। টেনসর বাফার API ( Write<T>()
) এবং পড়তে ( Read<T>()
), এবং CPU মেমরি লক করার ক্ষমতা প্রদান করে।
TensorBuffer
API কীভাবে প্রয়োগ করা হয় তার আরও সম্পূর্ণ দর্শনের জন্য, litter_tensor_buffer.h এর সোর্স কোডটি দেখুন।
ক্যোয়ারী মডেল ইনপুট/আউটপুট প্রয়োজনীয়তা
একটি টেনসর বাফার ( TensorBuffer
) বরাদ্দ করার জন্য প্রয়োজনীয়তাগুলি সাধারণত হার্ডওয়্যার অ্যাক্সিলারেটর দ্বারা নির্দিষ্ট করা হয়। ইনপুট এবং আউটপুটগুলির জন্য বাফারগুলির প্রান্তিককরণ, বাফার স্ট্রাইড এবং মেমরির ধরন সম্পর্কিত প্রয়োজনীয়তা থাকতে পারে। আপনি এই প্রয়োজনীয়তাগুলি স্বয়ংক্রিয়ভাবে পরিচালনা করতে CreateInputBuffers
মত সহায়ক ফাংশন ব্যবহার করতে পারেন।
নিম্নলিখিত সরলীকৃত কোড স্নিপেট প্রদর্শন করে কিভাবে আপনি ইনপুট ডেটার জন্য বাফার প্রয়োজনীয়তা পুনরুদ্ধার করতে পারেন:
LITERT_ASSIGN_OR_RETURN(auto reqs, compiled_model.GetInputBufferRequirements(signature_index, input_index));
TensorBufferRequirements
API কীভাবে প্রয়োগ করা হয় তার আরও সম্পূর্ণ দর্শনের জন্য, lit_tensor_buffer_requirements.h এর সোর্স কোডটি দেখুন।
ম্যানেজড টেনসর বাফার তৈরি করুন (টেনসরবাফার)
নিম্নলিখিত সরলীকৃত কোড স্নিপেট দেখায় কিভাবে পরিচালিত টেনসর বাফার তৈরি করতে হয়, যেখানে 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));
জিরো-কপি দিয়ে টেনসর বাফার তৈরি করুন
একটি বিদ্যমান বাফারকে টেনসর বাফার (শূন্য-কপি) হিসাবে মোড়ানোর জন্য, নিম্নলিখিত কোড স্নিপেট ব্যবহার করুন:
// 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
, অন্যান্য বাফার প্রকারের সাথে আন্তঃক্রিয়াশীলতার অনুমতি দেয়। উদাহরণস্বরূপ, শূন্য-কপি সহ একটি 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());
ওপেনসিএল বাফারগুলি AHardwareBuffer
থেকেও তৈরি করা যেতে পারে:
LITERT_ASSIGN_OR_RETURN(auto cl_buffer, tensor_buffer_ahwb.GetOpenClMemory());
ওপেনসিএল এবং ওপেনজিএল-এর মধ্যে আন্তঃঅপারেবিলিটি সমর্থন করে এমন মোবাইল ডিভাইসে, জিএল বাফার থেকে সিএল বাফার তৈরি করা যেতে পারে:
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-এর নিম্নলিখিত বাস্তবায়নগুলি পড়ুন।
বেসিক ইনফারেন্স (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 নেক্সট কম্পাইল করা মডেল 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);