تتوفّر واجهة برمجة التطبيقات LiteRT CompiledModel بلغة C++، ما يتيح للمطوّرين التحكّم بدقة في عملية تخصيص الذاكرة والتطوير على مستوى منخفض. للاطّلاع على مثال، راجِع تطبيق C++ لتقسيم الصور.
يوضّح الدليل التالي عملية الاستنتاج الأساسية لوحدة المعالجة المركزية باستخدام واجهة برمجة التطبيقات CompiledModel Kotlin. راجِع الدليل حول تسريع وحدة معالجة الرسومات وتسريع وحدة المعالجة العصبية للتعرّف على ميزات التسريع المتقدّمة.
إضافة اعتمادية الإصدار
اختَر المسار الذي يناسب مشروعك:
استخدام مكتبة مُعدّة مسبقًا (متوافقة مع عدة أنظمة أساسية): يمكنك استخدام مكتبة LiteRT المُعدّة مسبقًا لإجراء عملية إعداد فورية. تعرَّف على كيفية استخدام مكتبة C++ المبرمَجة مسبقًا من حزمة LiteRT Maven على Android، أو نزِّل/ادمج ملف C++ الثنائي المبرمَج مسبقًا على Android وiOS وmacOS وLinux وWindows.
الإنشاء من المصدر (متوافق مع أنظمة أساسية متعددة): يمكنك الإنشاء من المصدر باستخدام CMake للتحكّم الكامل والتوافق مع أنظمة أساسية متعددة (Android وiOS وmacOS وLinux وWindows). يمكنك الاطّلاع على التفاصيل في هذا الدليل.
الاستنتاج الأساسي
يوضّح هذا القسم كيفية إجراء الاستنتاج الأساسي.
إنشاء البيئة
يوفر العنصر Environment بيئة وقت تشغيل تتضمّن مكوّنات، مثل مسار المكوّن الإضافي للمجمّع وسياقات وحدة معالجة الرسومات. يجب توفير Environment عند إنشاء CompiledModel وTensorBuffer. ينشئ الرمز التالي
Environment لتنفيذ وحدة المعالجة المركزية ووحدة معالجة الرسومات بدون أي خيارات:
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
إنشاء CompiledModel
بعد الحصول على نموذج LiteRT أو تحويل نموذج إلى تنسيق .tflite،
يجب تهيئة وقت التشغيل باستخدام ملف النموذج باستخدام واجهة برمجة التطبيقات CompiledModel.
يمكنك تحديد تسريع الأجهزة في هذه المرحلة
(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 CompiledModel.
خطأ أثناء المعالجة
تستخدم LiteRT الدالة litert::Expected لعرض القيم أو نشر الأخطاء بطريقة مشابهة للدالتَين absl::StatusOr أو std::expected. يمكنك التحقّق من الخطأ يدويًا.
لتسهيل الأمر، يوفّر LiteRT وحدات الماكرو التالية:
تُعيّن الدالة
LITERT_ASSIGN_OR_RETURN(lhs, expr)نتيجةexprإلىlhsإذا لم ينتج عنها خطأ، وإلا فإنّها تعرض الخطأ.سيتم توسيعه ليصبح مشابهًا للمقتطف التالي.
auto maybe_model = CompiledModel::Create(env, "mymodel.tflite", HwAccelerators::kCpu); 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 Buffers باستخدام ميزة "عدم النسخ"
لتضمين مخزن مؤقت حالي كـ 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 env, Environment::Create({}));
LITERT_ASSIGN_OR_RETURN(auto compiled_model, CompiledModel::Create(env, "mymodel.tflite",
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 compiled_model1, CompiledModel::Create(env, "model1.tflite", 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 compiled_model2, CompiledModel::Create(env, "model2.tflite", 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);