האצת GPU באמצעות LiteRT Next

מעבדים גרפיים (GPU) משמשים בדרך כלל להאצת למידת עומק, בגלל הקיבולת העצומה שלהם לעיבוד מקביל בהשוואה למעבדים מרכזיים (CPU). ‏LiteRT Next מפשט את התהליך של שימוש בהאצה של GPU, ומאפשר למשתמשים לציין את האצת החומרה כפרמטר כשיוצרים מודל מוערך (CompiledModel). בנוסף, ‏LiteRT Next משתמש בהטמעה חדשה ומשופרת של האצת GPU, שלא זמינה ב-LiteRT.

בעזרת האצת ה-GPU של LiteRT Next, אפשר ליצור מאגרי קלט ופלט שמותאמים ל-GPU, להשיג אפס העתקות של הנתונים בזיכרון ה-GPU ולבצע משימות באופן אסינכררוני כדי למקסם את היתירות.

להטמעות לדוגמה של LiteRT Next עם תמיכה ב-GPU, אפשר לעיין באפליקציות ההדגמה הבאות:

הוספת יחסי תלות ל-GPU

כך מוסיפים תלות ב-GPU לאפליקציה ב-Kotlin או ב-C++.

Kotlin

למשתמשי Kotlin, מואץ ה-GPU מובנה ולא נדרשים שלבים נוספים מעבר למדריך תחילת העבודה.

C++‎

משתמשים ב-C++ צריכים ליצור את יחסי התלות של האפליקציה עם האצת GPU של LiteRT. כלל cc_binary שמארז את הלוגיקה של הליבה של האפליקציה (למשל, main.cc) דורשים את רכיבי סביבת זמן הריצה הבאים:

  • ספרייה משותפת של LiteRT C API: המאפיין data חייב לכלול את הספרייה המשותפת של LiteRT C API‏ (//litert/c:litert_runtime_c_api_shared_lib) ואת הרכיבים הספציפיים ל-GPU‏ (@litert_gpu//:jni/arm64-v8a/libLiteRtGpuAccelerator.so).
  • יחסי תלות בין מאפיינים: המאפיין deps כולל בדרך כלל יחסי תלות של GLES‏ gles_deps(), והמאפיין linkopts כולל בדרך כלל יחסי תלות של gles_linkopts(). שתי השיטות רלוונטיות מאוד להאצת GPU, כי ב-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
)

ההגדרה הזו מאפשרת לקובץ הבינארי המהדר להיטען באופן דינמי ולהשתמש ב-GPU להסקה מואצת של למידת מכונה.

שנתחיל?

כדי להתחיל להשתמש במאיץ ה-GPU, מעבירים את הפרמטר GPU כשיוצרים את המודל המהדר (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.

LiteRT Next GPU Accelerator

ה-GPU Accelerator החדש, שזמין רק ב-LiteRT Next, עבר אופטימיזציה כדי לטפל בעומסי עבודה של AI, כמו כפל מטריצות גדולות ומטמון KV ל-LLM, בצורה יעילה יותר מאשר בגרסאות קודמות. מאיץ ה-GPU של LiteRT Next כולל את השיפורים העיקריים הבאים בהשוואה לגרסה של LiteRT:

  • הרחבת הכיסוי של האופרטורים: טיפול ברשתות עצביות גדולות ומורכבות יותר.
  • יכולת פעולה הדדית משופרת של מאגרים: הפעלת שימוש ישיר במאגרי GPU לפריימים של מצלמה, למרקמים דו-ממדיים או למצבים גדולים של LLM.
  • תמיכה בהרצה אסינכרוני:חפיפה בין העיבוד המקדים של המעבד המרכזי (CPU) לבין הסקת המסקנות של המעבד הגרפי (GPU).

העברה ללא העתקה (zero-copy) עם האצה של GPU

השימוש ב-zero-copy מאפשר ל-GPU לגשת לנתונים ישירות בזיכרון שלו, בלי שה-CPU יצטרך להעתיק את הנתונים האלה באופן מפורש. מכיוון שלא מתבצעת העתקה של נתונים לזיכרון המעבד וממנו, השימוש ב-zero-copy יכול לצמצם משמעותית את זמן האחזור מקצה לקצה.

הקוד הבא הוא דוגמה להטמעה של GPU ללא העתקה (Zero-Copy) באמצעות OpenGL, ממשק API לעיבוד גרפיקה וקטורית. הקוד מעביר תמונות בפורמט מאגר של OpenGL ישירות ל-LiteRT Next:

// 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(), מאפשרות לתזמן את ההסקה ב-GPU תוך המשך משימות אחרות באמצעות ה-CPU או ה-NPU. בצינורות עיבוד נתונים מורכבים, לרוב משתמשים ב-GPU באופן אסינכרוני לצד מעבדים או יחידות NPUs.

קטע הקוד הבא מבוסס על הקוד שסופק בדוגמה של Zero-copy GPU acceleration. הקוד משתמש גם ב-CPU וגם ב-GPU באופן אסינכרוני, ומצרף ל-LiteRT Event למאגר הנתונים הזמני של הקלט. ‏LiteRT Event אחראי לניהול סוגים שונים של פרימיטיבים של סנכרון, והקוד הבא יוצר אובייקט אירוע מנוהל של LiteRT מסוג LiteRtEventTypeEglSyncFence. האובייקט Event מוודא שלא נבצע קריאה ממאגר הקלט עד שה-GPU יסיים. כל זה מתבצע בלי מעורבות של המעבד.

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 Next יש תמיכה בשיפור המהירות באמצעות GPU בדגמים הבאים. תוצאות הבדיקות מבוססות על בדיקות שבוצעו במכשיר Samsung Galaxy S24.

מודל LiteRT GPU Acceleration LiteRT GPU (ms)
hf_mms_300m הענקת גישה מלאה 19.6
hf_mobilevit_small הענקת גישה מלאה 8.7
hf_mobilevit_small_e2e הענקת גישה מלאה 8.0
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.0
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