Las unidades de procesamiento de gráficos (GPU) se usan comúnmente para la aceleración del aprendizaje profundo debido a su capacidad de procesamiento masivo en paralelo en comparación con las CPU. LiteRT simplifica el proceso de uso de la aceleración de GPU, ya que permite a los usuarios especificar la aceleración de hardware como un parámetro cuando crean un modelo compilado (CompiledModel).
Con la aceleración por GPU de LiteRT, puedes crear búferes de entrada y salida compatibles con la GPU, lograr una copia cero con tus datos en la memoria de la GPU y ejecutar tareas de forma asíncrona para maximizar el paralelismo.
Comenzar
Para los modelos de AA clásicos, consulta las siguientes apps de demostración.
- App de Kotlin para la segmentación de imágenes: Inferencias de CPU, GPU y NPU.
- App de segmentación de imágenes en C++: Inferencias de CPU/GPU/NPU con ejecución asíncrona.
Para los modelos de IA generativa, consulta las siguientes demostraciones y la guía:
- EmbeddingGemma semantic similarity C++ App: Inferencias en CPU, GPU y NPU
- Guía para ejecutar LLMs con LiteRT-LM
Agrega la dependencia de la GPU
Sigue estos pasos para agregar la dependencia de GPU a tu aplicación en Kotlin o C++.
Kotlin
Para los usuarios de Kotlin, el acelerador de GPU está integrado y no requiere pasos adicionales más allá de la guía de Primeros pasos.
C++
En el caso de los usuarios de C++, deben compilar las dependencias de la aplicación con la aceleración de GPU de LiteRT. La regla cc_binary que empaqueta la lógica principal de la aplicación (p.ej., main.cc) requiere los siguientes componentes de tiempo de ejecución:
- Biblioteca compartida de la API de LiteRT en C: El atributo
datadebe incluir la biblioteca compartida de la API de LiteRT en C (//litert/c:litert_runtime_c_api_shared_lib) y los componentes específicos de la GPU (litert_gpu_accelerator_prebuilts). - Dependencias de atributos: El atributo
depssuele incluir dependencias de GLESgles_deps(), ylinkoptssuele incluirgles_linkopts(). Ambos son muy relevantes para la aceleración de GPU, ya que LiteRT suele usar OpenGLES en Android. - Archivos de modelos y otros recursos: Se incluyen a través del atributo
data.
A continuación, se muestra un ejemplo de una regla de 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
)
Esta configuración permite que tu archivo binario compilado cargue y use la GPU de forma dinámica para la inferencia acelerada del aprendizaje automático.
Aceleradores de GPU prediseñados
El nuevo acelerador de GPU de LiteRT aún no es de código abierto. Sin embargo, los agentes compilados previamente están disponibles. En el caso de los usuarios de Kotlin, el paquete Maven de LiteRT ya contiene aceleradores de GPU. En el caso de los usuarios del SDK de C++, debes descargarlo por separado con este vínculo.
En Bazel, puedes usar la siguiente regla para agregar una dependencia a tu destino.
cpp
load("//litert/build_common:special_rule.bzl", "litert_gpu_accelerator_prebuilts")
Usa la GPU con la API de CompiledModel
Para comenzar a usar el acelerador de GPU, pasa el parámetro de GPU cuando crees el modelo compilado (CompiledModel). En el siguiente fragmento de código, se muestra una implementación básica de todo el proceso:
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()
Para obtener más información, consulta las guías Cómo comenzar a usar C++ o Cómo comenzar a usar Kotlin.
Copia cero con aceleración de GPU
El uso de la copia cero permite que una GPU acceda a los datos directamente en su propia memoria sin necesidad de que la CPU copie esos datos de forma explícita. Al no copiar datos hacia y desde la memoria de la CPU, la copia cero puede reducir significativamente la latencia de extremo a extremo.
El siguiente código es un ejemplo de implementación de GPU de copia cero con OpenGL, una API para renderizar gráficos vectoriales. El código pasa imágenes en el formato de búfer de OpenGL directamente a 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());
Ejecución asíncrona
Los métodos asíncronos de LiteRT, como RunAsync(), te permiten programar la inferencia de la GPU mientras continúas con otras tareas usando la CPU o la NPU. En las canalizaciones complejas, la GPU se suele usar de forma asíncrona junto con la CPU o las NPU.
El siguiente fragmento de código se basa en el código proporcionado en el ejemplo de aceleración de GPU sin copia. El código usa la CPU y la GPU de forma asíncrona y adjunta un LiteRT Event al búfer de entrada. LiteRT Event se encarga de administrar diferentes tipos de primitivas de sincronización, y el siguiente código crea un objeto Event de LiteRT administrado del tipo LiteRtEventTypeEglSyncFence. Este objeto Event garantiza que no leamos del búfer de entrada hasta que la GPU termine. Todo esto se realiza sin involucrar a la 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));
Backend compatible
LiteRT admite los siguientes backends de GPU para cada plataforma.
| Plataforma | Backend |
|---|---|
| Android | OpenCL + OpenGL |
| Linux | WebGPU (Vulkan) |
| macOS | Metal |
| Windows | WebGPU (Direct3D) |
| Android | OpenCL + OpenGL |
Modelos compatibles
LiteRT admite la aceleración por GPU con los siguientes modelos. Los resultados de las comparativas se basan en pruebas ejecutadas en un dispositivo Samsung Galaxy S24.
| Modelo | Aceleración de GPU de LiteRT | GPU de LiteRT (ms) |
|---|---|---|
| hf_mms_300m | Completamente delegada | 19.6 |
| hf_mobilevit_small | Completamente delegada | 8.7 |
| hf_mobilevit_small_e2e | Completamente delegada | 8.0 |
| hf_wav2vec2_base_960h | Completamente delegada | 9.1 |
| hf_wav2vec2_base_960h_dynamic | Completamente delegada | 9.8 |
| isnet | Completamente delegada | 43.1 |
| timm_efficientnet | Completamente delegada | 3.7 |
| timm_nfnet | Completamente delegada | 9.7 |
| timm_regnety_120 | Completamente delegada | 12.1 |
| torchaudio_deepspeech | Completamente delegada | 4.6 |
| torchaudio_wav2letter | Completamente delegada | 4.8 |
| torchvision_alexnet | Completamente delegada | 3.3 |
| torchvision_deeplabv3_mobilenet_v3_large | Completamente delegada | 5.7 |
| torchvision_deeplabv3_resnet101 | Completamente delegada | 35.1 |
| torchvision_deeplabv3_resnet50 | Completamente delegada | 24.5 |
| torchvision_densenet121 | Completamente delegada | 13.9 |
| torchvision_efficientnet_b0 | Completamente delegada | 3.6 |
| torchvision_efficientnet_b1 | Completamente delegada | 4.7 |
| torchvision_efficientnet_b2 | Completamente delegada | 5.0 |
| torchvision_efficientnet_b3 | Completamente delegada | 6.1 |
| torchvision_efficientnet_b4 | Completamente delegada | 7.6 |
| torchvision_efficientnet_b5 | Completamente delegada | 8.6 |
| torchvision_efficientnet_b6 | Completamente delegada | 11.2 |
| torchvision_efficientnet_b7 | Completamente delegada | 14.7 |
| torchvision_fcn_resnet50 | Completamente delegada | 19.9 |
| torchvision_googlenet | Completamente delegada | 3.9 |
| torchvision_inception_v3 | Completamente delegada | 8.6 |
| torchvision_lraspp_mobilenet_v3_large | Completamente delegada | 3.3 |
| torchvision_mnasnet0_5 | Completamente delegada | 2.4 |
| torchvision_mobilenet_v2 | Completamente delegada | 2.8 |
| torchvision_mobilenet_v3_large | Completamente delegada | 2.8 |
| torchvision_mobilenet_v3_small | Completamente delegada | 2.3 |
| torchvision_resnet152 | Completamente delegada | 15.0 |
| torchvision_resnet18 | Completamente delegada | 4.3 |
| torchvision_resnet50 | Completamente delegada | 6.9 |
| torchvision_squeezenet1_0 | Completamente delegada | 2.9 |
| torchvision_squeezenet1_1 | Completamente delegada | 2.5 |
| torchvision_vgg16 | Completamente delegada | 13.4 |
| torchvision_wide_resnet101_2 | Completamente delegada | 25.0 |
| torchvision_wide_resnet50_2 | Completamente delegada | 13.4 |
| u2net_full | Completamente delegada | 98.3 |
| u2net_lite | Completamente delegada | 51.4 |
| hf_distil_whisper_small_no_cache | Delegación parcial | 251.9 |
| hf_distilbert | Delegación parcial | 13.7 |
| hf_tinyroberta_squad2 | Delegación parcial | 17.1 |
| hf_tinyroberta_squad2_dynamic_batch | Delegación parcial | 52.1 |
| snapml_StyleTransferNet | Delegación parcial | 40.9 |
| timm_efficientformer_l1 | Delegación parcial | 17.6 |
| timm_efficientformerv2_s0 | Delegación parcial | 16.1 |
| timm_pvt_v2_b1 | Delegación parcial | 73.5 |
| timm_pvt_v2_b3 | Delegación parcial | 246.7 |
| timm_resnest14d | Delegación parcial | 88.9 |
| torchaudio_conformer | Delegación parcial | 21.5 |
| torchvision_convnext_tiny | Delegación parcial | 8.2 |
| torchvision_maxvit_t | Delegación parcial | 194.0 |
| torchvision_shufflenet_v2 | Delegación parcial | 9.5 |
| torchvision_swin_tiny | Delegación parcial | 164.4 |
| torchvision_video_resnet2plus1d_18 | Delegación parcial | 6832.0 |
| torchvision_video_swin3d_tiny | Delegación parcial | 2,617.8 |
| yolox_tiny | Delegación parcial | 11.2 |