使用 LiteRT Next 進行 GPU 加速

圖形處理器 (GPU) 通常用於深度學習加速,因為相較於 CPU,GPU 的平行總處理量相當龐大。LiteRT Next 會在使用者建立編譯模型 (CompiledModel) 時,允許使用者將硬體加速功能指定為參數,藉此簡化使用 GPU 加速功能的程序。LiteRT Next 也會使用全新且經過改善的 GPU 加速功能實作,而這並非 LiteRT 提供的功能。

透過 LiteRT Next 的 GPU 加速功能,您可以建立 GPU 友善的輸入和輸出緩衝區,在 GPU 記憶體中實現零複製資料,並以非同步方式執行工作,以便盡可能提高平行處理作業。

如需 LiteRT Next 支援 GPU 的實作範例,請參閱下列試用版應用程式:

新增 GPU 依附元件

請按照下列步驟,在 Kotlin 或 C++ 應用程式中新增 GPU 依附元件。

Kotlin

對於 Kotlin 使用者而言,GPU 加速器是內建的,因此除了「開始使用」指南之外,不需要額外採取任何步驟。

C++

C++ 使用者必須使用 LiteRT GPU 加速功能建構應用程式的依附元件。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()。由於 LiteRT 通常會在 Android 上使用 OpenGLES,因此這兩者與 GPU 加速功能高度相關。
  • 模型檔案和其他資產:透過 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 加速器,請在建立編譯模型 (CompiledModel) 時傳遞 GPU 參數。以下程式碼片段顯示整個程序的基本實作方式:

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 加速器

全新 GPU 加速器僅適用於 LiteRT Next,經過最佳化處理 AI 工作負載,例如大型矩陣相乘和 LLM 的 KV 快取,比先前版本更有效率。LiteRT Next GPU 加速器相較於 LiteRT 版本,具有下列主要改善項目:

  • 擴充運算子涵蓋範圍:處理更大型、更複雜的類神經網路。
  • 更佳的緩衝區互通性:啟用 GPU 緩衝區的直接使用方式,適用於相機影格、2D 紋理或大型 LLM 狀態。
  • 支援非同步執行:將 CPU 預先處理作業與 GPU 推論作業重疊執行。

搭配 GPU 加速功能的零複製

使用零複製功能可讓 GPU 直接存取自身記憶體中的資料,而無需由 CPU 明確複製該資料。由於零複製不會將資料複製至 CPU 記憶體,因此可大幅降低端對端延遲時間。

以下程式碼是使用 OpenGL 實作零複製 GPU 的範例,這是用於算繪向量圖形的 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 通常會與 CPU 或 NPU 一起非同步使用。

下列程式碼片段接續「零複製 GPU 加速」範例中提供的程式碼。程式碼會同時以非同步方式使用 CPU 和 GPU,並將 LiteRT Event 附加至輸入緩衝區。LiteRT Event 負責管理不同類型的同步化原語,而下列程式碼會建立 LiteRtEventTypeEglSyncFence 類型的受管理 LiteRT 事件物件。這個 Event 物件可確保我們不會在 GPU 完成之前,從輸入緩衝區讀取資料。而且完全不需要 CPU 的協助即可完成。

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 加速 LiteRT GPU (毫秒)
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