تتوفّر واجهة برمجة التطبيقات LiteRT CompiledModel بلغة C++، ما يتيح للمطوّرين التحكّم بدقة في عملية تخصيص الذاكرة والتطوير على مستوى منخفض. للاطّلاع على مثال، راجِع تطبيق C++ لتقسيم الصور.
يوضّح الدليل التالي عملية الاستنتاج الأساسية لوحدة المعالجة المركزية باستخدام واجهة برمجة التطبيقات CompiledModel Kotlin. راجِع الدليل حول تسريع وحدة معالجة الرسومات وتسريع وحدة المعالجة العصبية للتعرّف على ميزات التسريع المتقدّمة.
إضافة اعتمادية الإصدار
اختَر المسار الذي يناسب مشروعك:
استخدام مكتبة مُعدّة مسبقًا (Android): استخدِم المكتبة المُعدّة مسبقًا من حزمة LiteRT Maven لإجراء عملية إعداد فورية. تعرَّف على كيفية استخدام مكتبة C++ مسبقة الإنشاء من LiteRT Maven.
الإنشاء من المصدر (متوافق مع أنظمة تشغيل متعددة): يمكنك الإنشاء من المصدر للتحكّم بشكل كامل والاستفادة من التوافق مع أنظمة تشغيل متعددة (Android وiOS وmacOS وLinux وWindows). يُرجى الاطّلاع على التعليمات التالية.
إنشاء التطبيق من المصدر باستخدام Bazel
يتضمّن إنشاء تطبيق C++ باستخدام LiteRT لتسريع وحدة معالجة الرسومات ووحدة المعالجة العصبية ووحدة المعالجة المركزية باستخدام Bazel تحديد قاعدة cc_binary لضمان تجميع جميع المكوّنات الضرورية وربطها وتعبئتها. يسمح مثال الإعداد التالي لتطبيقك باختيار مسرّعات وحدة معالجة الرسومات ووحدة المعالجة العصبية ووحدة المعالجة المركزية أو استخدامها بشكل ديناميكي.
في ما يلي المكوّنات الرئيسية في إعدادات الإصدار في Bazel:
cc_binaryالقاعدة: هذه هي قاعدة Bazel الأساسية المستخدَمة لتحديد هدفك القابل للتنفيذ بلغة C++ (على سبيل المثال،name = "your_application_name").srcsالسمة: تُدرِج ملفات المصدر C++ لتطبيقك (مثل main.cc، وملفات.ccأو.hالأخرى).- السمة
data(التبعيات في وقت التشغيل): هذه السمة ضرورية لتضمين المكتبات المشترَكة ومواد العرض التي يحمّلها تطبيقك في وقت التشغيل.- وقت التشغيل الأساسي في LiteRT: مكتبة C API الرئيسية المشترَكة في LiteRT (مثل
//litert/c:litert_runtime_c_api_shared_lib). - مكتبات الإرسال: هي مكتبات مشتركة خاصة بمورّدين معيّنين تستخدمها LiteRT للتواصل مع برامج تشغيل الأجهزة (مثل
//litert/vendors/qualcomm/dispatch:dispatch_api_so). - مكتبات الخلفية لوحدة معالجة الرسومات: هي المكتبات المشتركة لتسريع وحدة معالجة الرسومات (مثل
"@litert_gpu//:jni/arm64-v8a/libLiteRtGpuAccelerator.so). - مكتبات الخلفية لوحدة المعالجة العصبية: هي المكتبات المشتركة الخاصة بتسريع وحدة المعالجة العصبية، مثل مكتبات QNN HTP من Qualcomm (مثل
@qairt//:lib/aarch64-android/libQnnHtp.so,@qairt//:lib/hexagon-v79/unsigned/libQnnHtpV79Skel.so). - ملفات النماذج ومواد العرض: ملفات النماذج المدرَّبة أو صور الاختبار أو برامج التظليل أو أي بيانات أخرى مطلوبة أثناء وقت التشغيل (مثل
:model_files,:shader_files).
- وقت التشغيل الأساسي في LiteRT: مكتبة C API الرئيسية المشترَكة في LiteRT (مثل
- السمة
deps(العناصر التابعة في وقت الترجمة): تعرض هذه السمة المكتبات التي يحتاجها الرمز البرمجي للترجمة.- LiteRT & Utilities: العناوين والمكتبات الثابتة لمكوّنات LiteRT، مثل مخازن الموتر (على سبيل المثال،
//litert/cc:litert_tensor_buffer). - مكتبات الرسومات (لوحدة معالجة الرسومات): العناصر التابعة ذات الصلة بواجهة برمجة تطبيقات الرسومات
إذا كان مسرِّع وحدة معالجة الرسومات يستخدمها (مثل
gles_deps()).
- LiteRT & Utilities: العناوين والمكتبات الثابتة لمكوّنات 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 بيئة وقت تشغيل تتضمّن مكوّنات، مثل مسار المكوّن الإضافي للمجمّع وسياقات وحدة معالجة الرسومات. السمة Environment مطلوبة عند إنشاء CompiledModel وTensorBuffer. ينشئ الرمز التالي Environment لتنفيذ وحدة المعالجة المركزية ووحدة معالجة الرسومات بدون أي خيارات:
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
إنشاء CompiledModel
باستخدام واجهة برمجة التطبيقات 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
المفاهيم والمكوّنات الرئيسية
راجِع الأقسام التالية للحصول على معلومات حول المفاهيم والمكوّنات الأساسية لواجهة برمجة التطبيقات CompiledModel 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.
مخزن مؤقت للمتّجه (TensorBuffer)
توفّر LiteRT إمكانية التشغيل التفاعلي لمخزن الإدخال/الإخراج المؤقت، وذلك باستخدام واجهة برمجة التطبيقات Tensor Buffer API (TensorBuffer) للتعامل مع تدفق البيانات من النموذج المترجَم وإليه. توفّر واجهة برمجة التطبيقات Tensor Buffer API إمكانية الكتابة (Write<T>()) والقراءة (Read<T>()) وتأمين ذاكرة وحدة المعالجة المركزية.
للحصول على عرض أكثر اكتمالاً لطريقة تنفيذ واجهة برمجة التطبيقات TensorBuffer، يمكنك الاطّلاع على الرمز المصدر الخاص بـ litert_tensor_buffer.h.
متطلبات إدخال/إخراج نموذج طلب البحث
عادةً ما يحدّد مسرّع الأجهزة متطلبات تخصيص مساحة تخزين مؤقت Tensor (TensorBuffer). يمكن أن تتضمّن مخازن مؤقتة للمدخلات والمخرجات متطلبات بشأن المحاذاة وخطوات المخزن المؤقت ونوع الذاكرة. يمكنك استخدام دوال مساعدة، مثل CreateInputBuffers، للتعامل مع هذه المتطلبات تلقائيًا.
يوضّح مقتطف الرمز المبسَّط التالي كيفية استرداد متطلبات المخزن المؤقت لبيانات الإدخال:
LITERT_ASSIGN_OR_RETURN(auto reqs, compiled_model.GetInputBufferRequirements(signature_index, input_index));
للحصول على عرض أكثر اكتمالاً لطريقة تنفيذ واجهة برمجة التطبيقات TensorBufferRequirements، يمكنك الاطّلاع على رمز المصدر الخاص بـ litert_tensor_buffer_requirements.h.
إنشاء مخازن مؤقتة مُدارة للموتّرات (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 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++.
الاستدلال الأساسي (وحدة المعالجة المركزية)
في ما يلي نسخة مختصرة من مقتطفات الرموز البرمجية من قسم البدء. وهي أبسط طريقة لتنفيذ الاستدلال باستخدام 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));
Zero-Copy with Host Memory
تساهم واجهة برمجة التطبيقات LiteRT CompiledModel في تقليل المشاكل التي تواجه خطوط نقل البيانات للاستدلال،
خاصةً عند التعامل مع العديد من الأنظمة الخلفية للأجهزة وتدفقات النسخ الصفرية. يستخدم مقتطف الرمز التالي الطريقة 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);