تسريع وحدة معالجة الرسومات باستخدام LiteRT

تُستخدَم وحدات معالجة الرسومات (GPU) بشكل شائع لتسريع عملية التعلّم العميق، وذلك بسبب معدل نقل البيانات المتوازي الهائل الذي توفّره مقارنةً بوحدات المعالجة المركزية (CPU). تسهّل LiteRT عملية استخدام تسريع وحدة معالجة الرسومات من خلال السماح للمستخدمين بتحديد تسريع الأجهزة كمعلَمة عند إنشاء "نموذج مجمّع" (CompiledModel).

باستخدام ميزة تسريع وحدة معالجة الرسومات في LiteRT، يمكنك إنشاء مخازن مؤقتة للإدخال والإخراج متوافقة مع وحدة معالجة الرسومات، وتنفيذ عمليات نسخ بدون وسيطة لبياناتك في ذاكرة وحدة معالجة الرسومات، وتنفيذ المهام بشكل غير متزامن لتحقيق أقصى قدر من التوازي.

البدء

  • بالنسبة إلى نماذج تعلُّم الآلة الكلاسيكية، يمكنك الاطّلاع على التطبيقات التجريبية التالية.

    • تطبيق Kotlin لتقسيم الصور: الاستنتاج باستخدام وحدة المعالجة المركزية (CPU) أو وحدة معالجة الرسومات (GPU) أو وحدة المعالجة العصبية (NPU)
    • تطبيق C++ لتقسيم الصور: استنتاج وحدة المعالجة المركزية/وحدة معالجة الرسومات/وحدة المعالجة العصبية مع تنفيذ غير متزامن
  • بالنسبة إلى نماذج الذكاء الاصطناعي التوليدي، يُرجى الاطّلاع على العروض التوضيحية والدليل التاليَين:

إضافة اعتمادية وحدة معالجة الرسومات

اتّبِع الخطوات التالية لإضافة تبعية وحدة معالجة الرسومات إلى تطبيق Kotlin أو C++.

Kotlin

بالنسبة إلى مستخدمي Kotlin، فإنّ أداة تسريع وحدة معالجة الرسومات مدمجة ولا تتطلّب اتّخاذ خطوات إضافية بخلاف تلك الموضّحة في دليل البدء.

C++‎

بالنسبة إلى مستخدمي C++، يجب إنشاء تبعيات التطبيق باستخدام تسريع LiteRT GPU. قاعدة cc_binary التي تحزم منطق التطبيق الأساسي (مثلاً يتطلّب main.cc) مكوّنات وقت التشغيل التالية:

  • مكتبة LiteRT C API المشترَكة: يجب أن تتضمّن السمة data مكتبة LiteRT C API المشترَكة (//litert/c:litert_runtime_c_api_shared_lib) والمكوّنات الخاصة بوحدة معالجة الرسومات (@litert_gpu//:jni/arm64-v8a/libLiteRtGpuAccelerator.so).
  • سمات التبعية: تتضمّن السمة deps عادةً تبعيات GLES gles_deps()، وتتضمّن السمة linkopts عادةً gles_linkopts(). وكلاهما مهمان جدًا لتسريع وحدة معالجة الرسومات، لأنّ LiteRT غالبًا ما تستخدم OpenGLES على Android.
  • ملفات التصميم ومواد العرض الأخرى: يتم تضمينها من خلال السمة data.

في ما يلي مثال على قاعدة 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",
    ],
    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
)

يتيح هذا الإعداد للرمز الثنائي المجمَّع تحميل وحدة معالجة الرسومات واستخدامها بشكل ديناميكي لإجراء استنتاج سريع في عملية تعلُّم الآلة.

استخدام وحدة معالجة الرسومات مع واجهة برمجة التطبيقات CompiledModel

للبدء في استخدام مسرِّع وحدة معالجة الرسومات، مرِّر مَعلمة وحدة معالجة الرسومات عند إنشاء النموذج المجمَّع (CompiledModel). يعرض مقتطف الرمز التالي عملية التنفيذ الأساسية للعملية بأكملها:

C++‎

// 1. Load model
LITERT_ASSIGN_OR_RETURN(auto model, Model::CreateFromFile("mymodel.tflite"));

// 2. Create a compiled model targeting GPU
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
LITERT_ASSIGN_OR_RETURN(auto compiled_model, CompiledModel::Create(env, model, kLiteRtHwAcceleratorGpu));

// 3. Prepare input/output buffers
LITERT_ASSIGN_OR_RETURN(auto input_buffers, compiled_model.CreateInputBuffers());
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());

// 4. Fill input data (if you have CPU-based data)
input_buffers[0].Write<float>(absl::MakeConstSpan(cpu_data, data_size));

// 5. Execute
compiled_model.Run(input_buffers, output_buffers);

// 6. Access model output
std::vector<float> data(output_data_size);
output_buffers.Read<float>(absl::MakeSpan(data));

Kotlin

// Load model and initialize runtime
val  model =
    CompiledModel.create(
        context.assets,
        "mymodel.tflite",
        CompiledModel.Options(Accelerator.GPU),
        env,
    )

// Preallocate input/output buffers
val inputBuffers = model.createInputBuffers()
val outputBuffers = model.createOutputBuffers()

// Fill the first input
inputBuffers[0].writeFloat(FloatArray(data_size) { data_value /* your data */ })

// Invoke
model.run(inputBuffers, outputBuffers)

// Read the output
val outputFloatArray = outputBuffers[0].readFloat()

لمزيد من المعلومات، يُرجى الاطّلاع على دليل البدء في استخدام C++‎ أو دليل البدء في استخدام Kotlin.

عدم النسخ مع تسريع وحدة معالجة الرسومات

يتيح استخدام ميزة &quot;النسخ بدون وسيط&quot; لوحدة معالجة الرسومات الوصول إلى البيانات مباشرةً في الذاكرة الخاصة بها بدون أن تحتاج وحدة المعالجة المركزية إلى نسخ هذه البيانات بشكل صريح. من خلال عدم نسخ البيانات إلى ذاكرة وحدة المعالجة المركزية ومنها، يمكن أن تقلّل عملية النسخ بدون وسيط بشكل كبير من وقت الاستجابة من البداية إلى النهاية.

الرمز البرمجي التالي هو مثال على تنفيذ Zero-Copy GPU باستخدام OpenGL، وهي واجهة برمجة تطبيقات لعرض الرسومات المتجهة. تنقل التعليمات البرمجية الصور بتنسيق المخزن المؤقت OpenGL مباشرةً إلى LiteRT:

// Suppose you have an OpenGL buffer consisting of:
// target (GLenum), id (GLuint), size_bytes (size_t), and offset (size_t)
// Load model and compile for GPU
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, kLiteRtHwAcceleratorGpu));

// Create a TensorBuffer that wraps the OpenGL buffer.
LITERT_ASSIGN_OR_RETURN(auto tensor_type, model.GetInputTensorType("input_tensor_name"));
LITERT_ASSIGN_OR_RETURN(auto gl_input_buffer, TensorBuffer::CreateFromGlBuffer(env,
    tensor_type, opengl_buffer.target, opengl_buffer.id, opengl_buffer.size_bytes, opengl_buffer.offset));
std::vector<TensorBuffer> input_buffers{gl_input_buffer};
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());

// Execute
compiled_model.Run(input_buffers, output_buffers);

// If your output is also GPU-backed, you can fetch an OpenCL buffer or re-wrap it as an OpenGL buffer:
LITERT_ASSIGN_OR_RETURN(auto out_cl_buffer, output_buffers[0].GetOpenClBuffer());

التنفيذ غير المتزامن

تتيح لك طرق LiteRT غير المتزامنة، مثل RunAsync()، جدولة استنتاج وحدة معالجة الرسومات مع مواصلة المهام الأخرى باستخدام وحدة المعالجة المركزية أو وحدة المعالجة العصبية. في مسارات المعالجة المعقّدة، يتم غالبًا استخدام وحدة معالجة الرسومات بشكل غير متزامن مع وحدة المعالجة المركزية أو وحدات المعالجة العصبية.

يستند مقتطف الرمز البرمجي التالي إلى الرمز البرمجي المقدَّم في مثال تسريع وحدة معالجة الرسومات بدون نسخ. يستخدم الرمز البرمجي كلاً من وحدة المعالجة المركزية ووحدة معالجة الرسومات بشكل غير متزامن، كما يرفق Event LiteRT بمخزن الإدخال المؤقت. تتولّى LiteRT Event مسؤولية إدارة أنواع مختلفة من عناصر التزامن الأساسية، وينشئ الرمز التالي عنصر LiteRT Event مُدارًا من النوع LiteRtEventTypeEglSyncFence. يضمن عنصر Event هذا عدم القراءة من مخزن الإدخال المؤقت إلى أن تنتهي وحدة معالجة الرسومات. ويتم كل ذلك بدون إشراك وحدة المعالجة المركزية.

LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
LITERT_ASSIGN_OR_RETURN(auto compiled_model,
    CompiledModel::Create(env, model, kLiteRtHwAcceleratorGpu));

// 1. Prepare input buffer (OpenGL buffer)
LITERT_ASSIGN_OR_RETURN(auto gl_input,
    TensorBuffer::CreateFromGlBuffer(env, tensor_type, opengl_tex));
std::vector<TensorBuffer> inputs{gl_input};
LITERT_ASSIGN_OR_RETURN(auto outputs, compiled_model.CreateOutputBuffers());

// 2. If the GL buffer is in use, create and set an event object to synchronize with the GPU.
LITERT_ASSIGN_OR_RETURN(auto input_event,
    Event::CreateManagedEvent(env, LiteRtEventTypeEglSyncFence));
inputs[0].SetEvent(std::move(input_event));

// 3. Kick off the GPU inference
compiled_model.RunAsync(inputs, outputs);

// 4. Meanwhile, do other CPU work...
// CPU Stays busy ..

// 5. Access model output
std::vector<float> data(output_data_size);
outputs[0].Read<float>(absl::MakeSpan(data));

النماذج المتوافقة

تتيح LiteRT تسريع وحدة معالجة الرسومات مع الطُرز التالية. تستند نتائج قياس الأداء إلى الاختبارات التي تم إجراؤها على جهاز Samsung Galaxy S24.

الطراز تسريع LiteRT باستخدام وحدة معالجة الرسومات وحدة معالجة الرسومات LiteRT (بالمللي ثانية)
hf_mms_300m مفوَّض بالكامل 19.6
hf_mobilevit_small مفوَّض بالكامل 8.7
hf_mobilevit_small_e2e مفوَّض بالكامل من نظام التشغيل Android
hf_wav2vec2_base_960h مفوَّض بالكامل ‫9.1
hf_wav2vec2_base_960h_dynamic مفوَّض بالكامل 9.8
isnet مفوَّض بالكامل 43.1
timm_efficientnet مفوَّض بالكامل 3.7
timm_nfnet مفوَّض بالكامل 9.7
timm_regnety_120 مفوَّض بالكامل 12.1
torchaudio_deepspeech مفوَّض بالكامل 4.6
torchaudio_wav2letter مفوَّض بالكامل 4.8
torchvision_alexnet مفوَّض بالكامل 3.3
torchvision_deeplabv3_mobilenet_v3_large مفوَّض بالكامل 5.7
torchvision_deeplabv3_resnet101 مفوَّض بالكامل 35.1
torchvision_deeplabv3_resnet50 مفوَّض بالكامل 24.5
torchvision_densenet121 مفوَّض بالكامل 13.9
torchvision_efficientnet_b0 مفوَّض بالكامل 3.6
torchvision_efficientnet_b1 مفوَّض بالكامل 4.7
torchvision_efficientnet_b2 مفوَّض بالكامل 5.0
torchvision_efficientnet_b3 مفوَّض بالكامل 6.1
torchvision_efficientnet_b4 مفوَّض بالكامل 7.6
torchvision_efficientnet_b5 مفوَّض بالكامل 8.6
torchvision_efficientnet_b6 مفوَّض بالكامل ‫11.2
torchvision_efficientnet_b7 مفوَّض بالكامل 14.7
torchvision_fcn_resnet50 مفوَّض بالكامل 19.9
torchvision_googlenet مفوَّض بالكامل 3.9
torchvision_inception_v3 مفوَّض بالكامل 8.6
torchvision_lraspp_mobilenet_v3_large مفوَّض بالكامل 3.3
torchvision_mnasnet0_5 مفوَّض بالكامل 2.4
torchvision_mobilenet_v2 مفوَّض بالكامل 2.8
torchvision_mobilenet_v3_large مفوَّض بالكامل 2.8
torchvision_mobilenet_v3_small مفوَّض بالكامل 2.3
torchvision_resnet152 مفوَّض بالكامل 15
torchvision_resnet18 مفوَّض بالكامل 4.3
torchvision_resnet50 مفوَّض بالكامل 6.9
torchvision_squeezenet1_0 مفوَّض بالكامل 2.9
torchvision_squeezenet1_1 مفوَّض بالكامل 2.5
torchvision_vgg16 مفوَّض بالكامل 13.4
torchvision_wide_resnet101_2 مفوَّض بالكامل 25.0
torchvision_wide_resnet50_2 مفوَّض بالكامل 13.4
u2net_full مفوَّض بالكامل 98.3
u2net_lite مفوَّض بالكامل 51.4
hf_distil_whisper_small_no_cache مفوّض جزئيًا 251.9
hf_distilbert مفوّض جزئيًا 13.7
hf_tinyroberta_squad2 مفوّض جزئيًا 17.1
hf_tinyroberta_squad2_dynamic_batch مفوّض جزئيًا 52.1
snapml_StyleTransferNet مفوّض جزئيًا 40.9
timm_efficientformer_l1 مفوّض جزئيًا 17.6
timm_efficientformerv2_s0 مفوّض جزئيًا 16.1
timm_pvt_v2_b1 مفوّض جزئيًا 73.5
timm_pvt_v2_b3 مفوّض جزئيًا 246.7
timm_resnest14d مفوّض جزئيًا 88.9
torchaudio_conformer مفوّض جزئيًا 21.5
torchvision_convnext_tiny مفوّض جزئيًا 8.2
torchvision_maxvit_t مفوّض جزئيًا 194.0
torchvision_shufflenet_v2 مفوّض جزئيًا 9.5
torchvision_swin_tiny مفوّض جزئيًا 164.4
torchvision_video_resnet2plus1d_18 مفوّض جزئيًا 6832.0
torchvision_video_swin3d_tiny مفوّض جزئيًا 2617.8
yolox_tiny مفوّض جزئيًا ‫11.2