Las unidades de procesamiento de gráficos (GPU) se usan comúnmente para la aceleración del aprendizaje profundo debido a su gran capacidad de procesamiento en paralelo en comparación con las CPU. LiteRT Next 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
). LiteRT Next también usa una implementación nueva y mejorada de la aceleración de GPU, que LiteRT no ofrece.
Con la aceleración de GPU de LiteRT Next, puedes crear búferes de entrada y salida compatibles con la GPU, lograr cero copias con tus datos en la memoria de la GPU y ejecutar tareas de forma asíncrona para maximizar el paralelismo.
Para ver ejemplos de implementaciones de LiteRT Next con compatibilidad con GPU, consulta las siguientes aplicaciones de demostración:
Cómo agregar una dependencia de GPU
Sigue los pasos que se indican a continuación para agregar una dependencia de GPU a tu aplicación de 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++, debes 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 del tiempo de ejecución:
- Biblioteca compartida de la API de LiteRT C: El atributo
data
debe incluir la biblioteca compartida de la API de LiteRT C (//litert/c:litert_runtime_c_api_shared_lib
) y los componentes específicos de la GPU (@litert_gpu//:jni/arm64-v8a/libLiteRtGpuAccelerator.so
). - Dependencias de atributos: Por lo general, el atributo
deps
incluye dependencias de GLESgles_deps()
, ylinkopts
suele incluirgles_linkopts()
. Ambos son muy relevantes para la aceleración de GPU, ya que LiteRT a menudo usa OpenGLES en Android. - Archivos de modelos y otros recursos: Se incluyen a través del atributo
data
.
El siguiente es un ejemplo de una regla 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
)
Esta configuración permite que el objeto binario compilado cargue y use de forma dinámica la GPU para la inferencia de aprendizaje automático acelerada.
Comenzar
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. 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()
Para obtener más información, consulta las guías Cómo comenzar a usar C++ o Cómo comenzar a usar Kotlin.
Acelerador de GPU LiteRT Next
El nuevo acelerador de GPU, disponible solo con LiteRT Next, está optimizado para controlar cargas de trabajo de IA, como multiplicaciones de matrices grandes y caché de KV para LLM, de manera más eficiente que las versiones anteriores. El acelerador de GPU LiteRT Next incluye las siguientes mejoras clave en comparación con la versión LiteRT:
- Cobertura extendida del operador: Controla redes neuronales más grandes y complejas.
- Mejor interoperabilidad de búferes: Habilita el uso directo de búferes de GPU para fotogramas de cámara, texturas 2D o estados LLM grandes.
- Compatibilidad con la ejecución asíncrona: Superposición del procesamiento previo de la CPU con la inferencia de la GPU.
Sin copia con aceleración de GPU
El uso de cero copias permite que una GPU acceda a los datos directamente en su propia memoria sin que la CPU deba copiarlos de forma explícita. Dado que no copia datos desde ni hacia 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 sin copia 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 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());
Ejecución asíncrona
Los métodos asíncronos de LiteRT, como RunAsync()
, te permiten programar la inferencia de GPU mientras continúas con otras tareas con la CPU o la NPU. En las canalizaciones complejas, a menudo se usa la GPU de forma asíncrona junto con la CPU o las NPU.
En el siguiente fragmento de código, se usa 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 Event
de LiteRT al búfer de entrada. LiteRT Event
es responsable de administrar diferentes tipos de primitivas de sincronización, y el siguiente código crea un objeto de evento LiteRT administrado de tipo LiteRtEventTypeEglSyncFence
. Este objeto Event
garantiza que no leamos del búfer de entrada hasta que la GPU termine. Todo esto se hace sin involucrar a la 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));
Modelos compatibles
LiteRT Next admite la aceleración de GPU con los siguientes modelos. Los resultados de las comparativas se basan en pruebas que se ejecutaron en un dispositivo Samsung Galaxy S24.
Modelo | Aceleración de GPU de LiteRT | GPU de LiteRT (ms) |
---|---|---|
hf_mms_300m | Completamente delegado | 19.6 |
hf_mobilevit_small | Completamente delegado | 8.7 |
hf_mobilevit_small_e2e | Completamente delegado | 8.0 |
hf_wav2vec2_base_960h | Completamente delegado | 9.1 |
hf_wav2vec2_base_960h_dynamic | Completamente delegado | 9.8 |
isnet | Completamente delegado | 43.1 |
timm_efficientnet | Completamente delegado | 3.7 |
timm_nfnet | Completamente delegado | 9.7 |
timm_regnety_120 | Completamente delegado | 12.1 |
torchaudio_deepspeech | Completamente delegado | 4.6 |
torchaudio_wav2letter | Completamente delegado | 4.8 |
torchvision_alexnet | Completamente delegado | 3.3 |
torchvision_deeplabv3_mobilenet_v3_large | Completamente delegado | 5.7 |
torchvision_deeplabv3_resnet101 | Completamente delegado | 35.1 |
torchvision_deeplabv3_resnet50 | Completamente delegado | 24.5 |
torchvision_densenet121 | Completamente delegado | 13.9 |
torchvision_efficientnet_b0 | Completamente delegado | 3.6 |
torchvision_efficientnet_b1 | Completamente delegado | 4.7 |
torchvision_efficientnet_b2 | Completamente delegado | 5.0 |
torchvision_efficientnet_b3 | Completamente delegado | 6.1 |
torchvision_efficientnet_b4 | Completamente delegado | 7.6 |
torchvision_efficientnet_b5 | Completamente delegado | 8.6 |
torchvision_efficientnet_b6 | Completamente delegado | 11.2 |
torchvision_efficientnet_b7 | Completamente delegado | 14.7 |
torchvision_fcn_resnet50 | Completamente delegado | 19.9 |
torchvision_googlenet | Completamente delegado | 3.9 |
torchvision_inception_v3 | Completamente delegado | 8.6 |
torchvision_lraspp_mobilenet_v3_large | Completamente delegado | 3.3 |
torchvision_mnasnet0_5 | Completamente delegado | 2.4 |
torchvision_mobilenet_v2 | Completamente delegado | 2.8 |
torchvision_mobilenet_v3_large | Completamente delegado | 2.8 |
torchvision_mobilenet_v3_small | Completamente delegado | 2.3 |
torchvision_resnet152 | Completamente delegado | 15.0 |
torchvision_resnet18 | Completamente delegado | 4.3 |
torchvision_resnet50 | Completamente delegado | 6.9 |
torchvision_squeezenet1_0 | Completamente delegado | 2.9 |
torchvision_squeezenet1_1 | Completamente delegado | 2.5 |
torchvision_vgg16 | Completamente delegado | 13.4 |
torchvision_wide_resnet101_2 | Completamente delegado | 25.0 |
torchvision_wide_resnet50_2 | Completamente delegado | 13.4 |
u2net_full | Completamente delegado | 98.3 |
u2net_lite | Completamente delegado | 51.4 |
hf_distil_whisper_small_no_cache | Delegada de forma parcial | 251.9 |
hf_distilbert | Delegada de forma parcial | 13.7 |
hf_tinyroberta_squad2 | Delegada de forma parcial | 17.1 |
hf_tinyroberta_squad2_dynamic_batch | Delegada de forma parcial | 52.1 |
snapml_StyleTransferNet | Delegada de forma parcial | 40.9 |
timm_efficientformer_l1 | Delegada de forma parcial | 17.6 |
timm_efficientformerv2_s0 | Delegada de forma parcial | 16.1 |
timm_pvt_v2_b1 | Delegada de forma parcial | 73.5 |
timm_pvt_v2_b3 | Delegada de forma parcial | 246.7 |
timm_resnest14d | Delegada de forma parcial | 88.9 |
torchaudio_conformer | Delegada de forma parcial | 21.5 |
torchvision_convnext_tiny | Delegada de forma parcial | 8.2 |
torchvision_maxvit_t | Delegada de forma parcial | 194.0 |
torchvision_shufflenet_v2 | Delegada de forma parcial | 9.5 |
torchvision_swin_tiny | Delegada de forma parcial | 164.4 |
torchvision_video_resnet2plus1d_18 | Delegada de forma parcial | 6,832.0 |
torchvision_video_swin3d_tiny | Delegada de forma parcial | 2,617.8 |
yolox_tiny | Delegada de forma parcial | 11.2 |