As unidades de processamento gráfico (GPUs) são comumente usadas para acelerar o aprendizado
profundo devido à capacidade paralela massiva em comparação com as CPUs. O LiteRT
Next simplifica o processo de uso da aceleração da GPU, permitindo que os usuários
especifiquem a aceleração de hardware como um parâmetro ao criar um modelo compilado
(CompiledModel
). O LiteRT Next também usa uma implementação de aceleração de GPU nova e aprimorada, não oferecida pelo LiteRT.
Com a aceleração de GPU do LiteRT Next, é possível criar buffers de entrada e saída compatíveis com GPU, conseguir zero cópia com seus dados na memória da GPU e executar tarefas de forma assíncrona para maximizar o paralelismo.
Para exemplos de implementações do LiteRT Next com suporte a GPU, consulte os seguintes aplicativos de demonstração:
Adicionar dependência de GPU
Siga as etapas abaixo para adicionar a dependência de GPU ao seu aplicativo Kotlin ou C++.
Kotlin
Para usuários do Kotlin, o acelerador de GPU é integrado e não requer outras etapas além do guia Primeiros passos.
C++
Para usuários do C++, é necessário criar as dependências do aplicativo com a aceleração de GPU
do LiteRT. A regra cc_binary
que empacota a lógica principal do aplicativo
(por exemplo, main.cc
) requer os seguintes componentes de execução:
- Biblioteca compartilhada da API LiteRT C: o atributo
data
precisa incluir a biblioteca compartilhada da API LiteRT C (//litert/c:litert_runtime_c_api_shared_lib
) e componentes específicos da GPU (@litert_gpu//:jni/arm64-v8a/libLiteRtGpuAccelerator.so
). - Dependências de atributos: o atributo
deps
normalmente inclui dependências de GLESgles_deps()
, elinkopts
normalmente incluigles_linkopts()
. Ambos são altamente relevantes para a aceleração de GPU, já que o LiteRT geralmente usa OpenGLES no Android. - Arquivos de modelo e outros recursos: incluídos pelo atributo
data
.
Confira abaixo um exemplo de regra 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
)
Essa configuração permite que o binário compilado carregue e use a GPU dinamicamente para inferência de machine learning acelerada.
Primeiros passos
Para começar a usar o acelerador de GPU, transmita o parâmetro de GPU ao criar
o modelo compilado (CompiledModel
). O snippet de código a seguir mostra uma implementação
básica de todo o processo:
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 saber mais, consulte os guias Primeiros passos com o C++ ou Primeiros passos com o Kotlin.
Acelerador de GPU LiteRT Next
O novo GPU Accelerator, disponível apenas com o LiteRT Next, é otimizado para processar cargas de trabalho de IA, como multiplicações de matrizes grandes e cache KV para LLMs, de maneira mais eficiente do que as versões anteriores. O Acelerador de GPU LiteRT Next apresenta as seguintes melhorias importantes em relação à versão do LiteRT:
- Cobertura estendida de operadores:processe redes neurais maiores e mais complexas.
- Melhor interoperabilidade de buffer:ative o uso direto de buffers de GPU para frames de câmera, texturas 2D ou estados de LLM grandes.
- Suporte a execução assíncrona:sobreposição do pré-processamento da CPU com a inferência da GPU.
Zero-copy com aceleração de GPU
O uso de zero-copy permite que uma GPU acesse dados diretamente na própria memória sem a necessidade de a CPU copiar esses dados explicitamente. Ao não copiar dados para e da memória da CPU, a cópia zero pode reduzir significativamente a latência de ponta a ponta.
O código a seguir é um exemplo de implementação de GPU sem cópia com OpenGL, uma API para renderizar gráficos vetoriais. O código transmite imagens no formato de buffer OpenGL diretamente para o 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());
Execução assíncrona
Os métodos assíncronos do LiteRT, como RunAsync()
, permitem programar a inferência da GPU
enquanto outras tarefas são executadas usando a CPU ou a NPU. Em pipelines complexos, a GPU
é frequentemente usada de forma assíncrona com CPU ou NPU.
O snippet de código abaixo se baseia no código fornecido no exemplo de aceleração de GPU
sem cópia. O código usa CPU e GPU
de forma assíncrona e anexa um Event
do LiteRT ao buffer de entrada. O Event
do LiteRT
é responsável por gerenciar diferentes tipos de primitivas de sincronização, e
o código abaixo cria um objeto de evento LiteRT gerenciado do tipo
LiteRtEventTypeEglSyncFence
. Esse objeto Event
garante que não lemos
do buffer de entrada até que a GPU termine. Tudo isso é feito sem envolver
a 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 compatíveis
O LiteRT Next oferece suporte à aceleração de GPU com os seguintes modelos. Os resultados de comparação de mercado são baseados em testes executados em um dispositivo Samsung Galaxy S24.
Modelo | Aceleração de GPU do LiteRT | GPU do LiteRT (ms) |
---|---|---|
hf_mms_300m | Totalmente delegada | 19,6 |
hf_mobilevit_small | Totalmente delegada | 8,7 |
hf_mobilevit_small_e2e | Totalmente delegada | 8.0 |
hf_wav2vec2_base_960h | Totalmente delegada | 9.1 |
hf_wav2vec2_base_960h_dynamic | Totalmente delegada | 9,8 |
isnet | Totalmente delegada | 43.1 |
timm_efficientnet | Totalmente delegada | 3.7 |
timm_nfnet | Totalmente delegada | 9.7 |
timm_regnety_120 | Totalmente delegada | 12.1 |
torchaudio_deepspeech | Totalmente delegada | 4,6 |
torchaudio_wav2letter | Totalmente delegada | 4,8 |
torchvision_alexnet | Totalmente delegada | 3.3 |
torchvision_deeplabv3_mobilenet_v3_large | Totalmente delegada | 5.7 |
torchvision_deeplabv3_resnet101 | Totalmente delegada | 35,1 |
torchvision_deeplabv3_resnet50 | Totalmente delegada | 24,5 |
torchvision_densenet121 | Totalmente delegada | 13,9 |
torchvision_efficientnet_b0 | Totalmente delegada | 3.6 |
torchvision_efficientnet_b1 | Totalmente delegada | 4,7 |
torchvision_efficientnet_b2 | Totalmente delegada | 5.0 |
torchvision_efficientnet_b3 | Totalmente delegada | 6.1 |
torchvision_efficientnet_b4 | Totalmente delegada | 7,6 |
torchvision_efficientnet_b5 | Totalmente delegada | 8.6 |
torchvision_efficientnet_b6 | Totalmente delegada | 11.2 |
torchvision_efficientnet_b7 | Totalmente delegada | 14.7 |
torchvision_fcn_resnet50 | Totalmente delegada | 19,9 |
torchvision_googlenet | Totalmente delegada | 3,9 |
torchvision_inception_v3 | Totalmente delegada | 8.6 |
torchvision_lraspp_mobilenet_v3_large | Totalmente delegada | 3.3 |
torchvision_mnasnet0_5 | Totalmente delegada | 2.4 |
torchvision_mobilenet_v2 | Totalmente delegada | 2.8 |
torchvision_mobilenet_v3_large | Totalmente delegada | 2.8 |
torchvision_mobilenet_v3_small | Totalmente delegada | 2.3 |
torchvision_resnet152 | Totalmente delegada | 15 |
torchvision_resnet18 | Totalmente delegada | 4.3 |
torchvision_resnet50 | Totalmente delegada | 6,9 |
torchvision_squeezenet1_0 | Totalmente delegada | 2.9 |
torchvision_squeezenet1_1 | Totalmente delegada | 2,5 |
torchvision_vgg16 | Totalmente delegada | 13,4 |
torchvision_wide_resnet101_2 | Totalmente delegada | 25.0 |
torchvision_wide_resnet50_2 | Totalmente delegada | 13,4 |
u2net_full | Totalmente delegada | 98,3 |
u2net_lite | Totalmente delegada | 51,4 |
hf_distil_whisper_small_no_cache | Parcialmente delegada | 251,9 |
hf_distilbert | Parcialmente delegada | 13.7 |
hf_tinyroberta_squad2 | Parcialmente delegada | 17,1 |
hf_tinyroberta_squad2_dynamic_batch | Parcialmente delegada | 52.1 |
snapml_StyleTransferNet | Parcialmente delegada | 40,9 |
timm_efficientformer_l1 | Parcialmente delegada | 17,6 |
timm_efficientformerv2_s0 | Parcialmente delegada | 16.1 |
timm_pvt_v2_b1 | Parcialmente delegada | 73,5 |
timm_pvt_v2_b3 | Parcialmente delegada | 246,7 |
timm_resnest14d | Parcialmente delegada | 88,9 |
torchaudio_conformer | Parcialmente delegada | 21,5 |
torchvision_convnext_tiny | Parcialmente delegada | 8.2 |
torchvision_maxvit_t | Parcialmente delegada | 194,0 |
torchvision_shufflenet_v2 | Parcialmente delegada | 9,5 |
torchvision_swin_tiny | Parcialmente delegada | 164,4 |
torchvision_video_resnet2plus1d_18 | Parcialmente delegada | 6832,0 |
torchvision_video_swin3d_tiny | Parcialmente delegada | 2617,8 |
yolox_tiny | Parcialmente delegada | 11.2 |