图形处理单元 (GPU) 通常用于深度学习加速,因为与 CPU 相比,GPU 具有巨大的并行吞吐量。LiteRT 允许用户在创建编译模型 (CompiledModel) 时将硬件加速指定为参数,从而简化了使用 GPU 加速的过程。
借助 LiteRT 的 GPU 加速功能,您可以创建 GPU 友好的输入和输出缓冲区,在 GPU 内存中实现数据零复制,并异步执行任务以最大限度地提高并行性。
开始使用
对于经典机器学习模型,请参阅以下演示应用。
- 图片分割 Kotlin 应用:CPU/GPU/NPU 推理。
- 图像分割 C++ 应用:使用异步执行的 CPU/GPU/NPU 推理。
对于生成式 AI 模型,请参阅以下演示和指南:
- EmbeddingGemma 语义相似度 C++ 应用:CPU/GPU/NPU 推理。
- 有关使用 LiteRT-LM 运行 LLM 的指南。
添加 GPU 依赖项
请按照以下步骤将 GPU 依赖项添加到 Kotlin 或 C++ 应用中。
Kotlin
对于 Kotlin 用户,GPU 加速器是内置的,除了使用入门指南之外,无需执行其他步骤。
C++
对于 C++ 用户,您必须使用 LiteRT GPU 加速功能构建应用的依赖项。用于打包核心应用逻辑(例如,cc_binarymain.cc) 需要以下运行时组件:
- LiteRT C API 共享库:
data属性必须包含 LiteRT C API 共享库 (//litert/c:litert_runtime_c_api_shared_lib) 和 GPU 特定的组件 (litert_gpu_accelerator_prebuilts)。 - 属性依赖项:
deps属性通常包含 GLES 依赖项gles_deps(),而linkopts通常包含gles_linkopts()。这两者都与 GPU 加速高度相关,因为 LiteRT 通常在 Android 上使用 OpenGLES。 - 模型文件和其他资源:通过
data属性包含。
以下是 cc_binary 规则的示例:
load("//litert/build_common:special_rule.bzl", "litert_gpu_accelerator_prebuilts")
cc_binary(
name = "your_application",
srcs = [
"main.cc",
],
data = [
...
# litert c api shared library
"//litert/c:litert_runtime_c_api_shared_lib",
] + litert_gpu_accelerator_prebuilts(),
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 加速器
新的 LiteRT GPU 加速器尚未开源。但预建代理可用。对于 Kotlin 用户,LiteRT Maven 软件包已包含 GPU 加速器。对于 C++ SDK 用户,您需要使用此链接单独下载。
在 Bazel 中,您可以使用以下规则向目标添加依赖项。
cpp
load("//litert/build_common:special_rule.bzl", "litert_gpu_accelerator_prebuilts")
将 GPU 与 CompiledModel API 搭配使用
如需开始使用 GPU 加速器,请在创建编译后的模型 (CompiledModel) 时传递 GPU 参数。以下代码段展示了整个过程的基本实现:
C++
// 1. Create a compiled model targeting GPU
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
LITERT_ASSIGN_OR_RETURN(auto compiled_model, CompiledModel::Create(env, "mymodel.tflite", kLiteRtHwAcceleratorGpu));
// 2. Prepare input/output buffers
LITERT_ASSIGN_OR_RETURN(auto input_buffers, compiled_model.CreateInputBuffers());
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());
// 3. Fill input data (if you have CPU-based data)
input_buffers[0].Write<float>(absl::MakeConstSpan(cpu_data, data_size));
// 4. Execute
compiled_model.Run(input_buffers, output_buffers);
// 5. 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 使用入门指南。
利用 GPU 加速实现零复制
使用零复制可让 GPU 直接访问其自身内存中的数据,而无需 CPU 明确复制该数据。通过不将数据复制到 CPU 内存和从 CPU 内存复制数据,零复制可以显著缩短端到端延迟时间。
以下代码是使用 OpenGL(一种用于渲染矢量图形的 API)实现零复制 GPU 的示例。该代码以 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 env, Environment::Create({}));
LITERT_ASSIGN_OR_RETURN(auto compiled_model,
CompiledModel::Create(env, "mymodel.tflite", 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()),您可以在使用 CPU 或 NPU 继续执行其他任务的同时,安排 GPU 推理。在复杂的流水线中,GPU 通常与 CPU 或 NPU 一起异步使用。
以下代码段基于零复制 GPU 加速示例中提供的代码。该代码以异步方式同时使用 CPU 和 GPU,并将 LiteRT Event 附加到输入缓冲区。LiteRT Event 负责管理不同类型的同步原语,以下代码创建了一个类型为 LiteRtEventTypeEglSyncFence 的受管理 LiteRT Event 对象。此 Event 对象可确保在 GPU 完成之前,我们不会从输入缓冲区读取数据。所有这些操作均无需 CPU 参与。
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
LITERT_ASSIGN_OR_RETURN(auto compiled_model,
CompiledModel::Create(env, "mymodel.tflite", 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 支持以下 GPU 后端(针对每个平台)。
| 平台 | 后端 |
|---|---|
| Android | OpenCL + OpenGL |
| Linux | WebGPU (Vulkan) |
| macOS | 金属 |
| Windows | WebGPU (Direct3D) |
| Android | OpenCL + OpenGL |
支持的模型
LiteRT 支持以下型号的 GPU 加速。基准测试结果基于在 Samsung Galaxy S24 设备上运行的测试。