Le unità di elaborazione grafica (GPU) sono comunemente utilizzate per l'accelerazione del deep learning grazie al loro enorme throughput parallelo rispetto alle CPU. LiteRT
Next semplifica il processo di utilizzo dell'accelerazione GPU consentendo agli utenti di
specificare l'accelerazione hardware come parametro durante la creazione di un modello compilato
(CompiledModel
). LiteRT Next utilizza inoltre un'implementazione dell'accelerazione GPU nuova e migliorata, non offerta da LiteRT.
Con l'accelerazione GPU di LiteRT Next, puoi creare buffer di input e output adatti alle GPU, eseguire la copia zero con i dati nella memoria GPU ed eseguire le attività in modo asincrono per massimizzare il parallelismo.
Per esempi di implementazioni di LiteRT Next con supporto GPU, consulta le seguenti applicazioni di dimostrazione:
Aggiungere una dipendenza GPU
Segui questa procedura per aggiungere la dipendenza dalla GPU alla tua applicazione Kotlin o C++.
Kotlin
Per gli utenti di Kotlin, l'acceleratore GPU è integrato e non richiede passaggi aggiuntivi oltre a quelli descritti nella guida Inizia.
C++
Gli utenti C++ devono compilare le dipendenze dell'applicazione con l'accelerazione GPU LiteRT. La regola cc_binary
che impacchetta la logica di base dell'applicazione
(ad es. main.cc
) richiede i seguenti componenti di runtime:
- Libreria condivisa dell'API C LiteRT: l'attributo
data
deve includere la libreria condivisa dell'API C LiteRT (//litert/c:litert_runtime_c_api_shared_lib
) e i componenti specifici della GPU (@litert_gpu//:jni/arm64-v8a/libLiteRtGpuAccelerator.so
). - Dipendenze degli attributi: l'attributo
deps
in genere include le dipendenze GLESgles_deps()
elinkopts
in genere includegles_linkopts()
. Entrambi sono molto pertinenti per l'accelerazione GPU, poiché LiteRT utilizza spesso OpenGLES su Android. - File modello e altri asset: inclusi tramite l'attributo
data
.
Di seguito è riportato un esempio di regola 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
)
Questa configurazione consente al codice binario compilato di caricare e utilizzare dinamicamente la GPU per l'inferenza del machine learning accelerata.
Inizia
Per iniziare a utilizzare l'acceleratore GPU, passa il parametro GPU durante la creazione del modello compilato (CompiledModel
). Lo snippet di codice seguente mostra un'implementazione di base dell'intera procedura:
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()
Per ulteriori informazioni, consulta le guide Guida introduttiva a C++ o Guida introduttiva a Kotlin.
Acceleratore GPU LiteRT Next
Il nuovo acceleratore GPU, disponibile solo con LiteRT Next, è ottimizzato per gestire i carichi di lavoro di IA, come le moltiplicazioni di matrici di grandi dimensioni e la cache KV per i modelli LLM, in modo più efficiente rispetto alle versioni precedenti. L'acceleratore GPU LiteRT Next presenta i seguenti miglioramenti chiave rispetto alla versione LiteRT:
- Copertura estesa dell'operatore:gestisci reti neurali più grandi e complesse.
- Maggiore interoperabilità dei buffer:consente l'utilizzo diretto dei buffer della GPU per frame della fotocamera, texture 2D o stati LLM di grandi dimensioni.
- Supporto dell'esecuzione asincrona:sovrapponi la pre-elaborazione della CPU all'inferenza della GPU.
Zero-copy con accelerazione GPU
L'utilizzo di zero-copy consente a una GPU di accedere ai dati direttamente nella propria memoria senza che la CPU debba copiarli esplicitamente. Se non copi i dati nella memoria della CPU e da essa, la copia zero può ridurre notevolmente la latenza end-to-end.
Il codice seguente è un esempio di implementazione della GPU zero-copy con OpenGL, un'API per il rendering di grafica vettoriale. Il codice passa le immagini nel formato del buffer OpenGL direttamente 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());
Esecuzione asincrona
I metodi asincroni di LiteRT, come RunAsync()
, ti consentono di pianificare l'inferenza GPU
mantenendo attive altre attività che utilizzano la CPU o la NPU. Nelle pipeline complesse, la GPU viene spesso utilizzata in modo asincrono insieme a CPU o NPUs.
Il seguente snippet di codice si basa sul codice fornito nell'esempio di accelerazione GPU senza copia. Il codice utilizza sia la CPU che la GPU in modo asincrono e collega un LiteRT Event
al buffer di input. LiteRT Event
è responsabile della gestione dei diversi tipi di primitive di sincronizzazione e
il seguente codice crea un oggetto LiteRT Event gestito di tipo
LiteRtEventTypeEglSyncFence
. Questo oggetto Event
garantisce che non venga eseguita alcuna lettura
dal buffer di input finché la GPU non ha terminato. Il tutto senza coinvolgere 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));
Modelli supportati
LiteRT Next supporta l'accelerazione GPU con i seguenti modelli. I risultati del benchmark si basano su test eseguiti su un dispositivo Samsung Galaxy S24.
Modello | Accelerazione GPU LiteRT | GPU LiteRT (ms) |
---|---|---|
hf_mms_300m | Completamente delegato | 19,6 |
hf_mobilevit_small | Completamente delegato | 8.7 |
hf_mobilevit_small_e2e | Completamente delegato | 8.0 |
hf_wav2vec2_base_960h | Completamente delegato | 9.1 |
hf_wav2vec2_base_960h_dynamic | Completamente delegato | 9.8 |
isnet | Completamente delegato | 43,1 |
timm_efficientnet | Completamente delegato | 3.7 |
timm_nfnet | Completamente delegato | 9.7 |
timm_regnety_120 | Completamente delegato | 12.1 |
torchaudio_deepspeech | Completamente delegato | 4,6 |
torchaudio_wav2letter | Completamente delegato | 4,8 |
torchvision_alexnet | Completamente delegato | 3.3 |
torchvision_deeplabv3_mobilenet_v3_large | Completamente delegato | 5.7 |
torchvision_deeplabv3_resnet101 | Completamente delegato | 35.1 |
torchvision_deeplabv3_resnet50 | Completamente delegato | 24,5 |
torchvision_densenet121 | Completamente delegato | 13,9 |
torchvision_efficientnet_b0 | Completamente delegato | 3,6 |
torchvision_efficientnet_b1 | Completamente delegato | 4,7 |
torchvision_efficientnet_b2 | Completamente delegato | 5,0 |
torchvision_efficientnet_b3 | Completamente delegato | 6,1 |
torchvision_efficientnet_b4 | Completamente delegato | 7,6 |
torchvision_efficientnet_b5 | Completamente delegato | 8.6 |
torchvision_efficientnet_b6 | Completamente delegato | 11.2 |
torchvision_efficientnet_b7 | Completamente delegato | 14,7 |
torchvision_fcn_resnet50 | Completamente delegato | 19,9 |
torchvision_googlenet | Completamente delegato | 3,9 |
torchvision_inception_v3 | Completamente delegato | 8.6 |
torchvision_lraspp_mobilenet_v3_large | Completamente delegato | 3.3 |
torchvision_mnasnet0_5 | Completamente delegato | 2,4 |
torchvision_mobilenet_v2 | Completamente delegato | 2,8 |
torchvision_mobilenet_v3_large | Completamente delegato | 2,8 |
torchvision_mobilenet_v3_small | Completamente delegato | 2.3 |
torchvision_resnet152 | Completamente delegato | 15,0 |
torchvision_resnet18 | Completamente delegato | 4.3 |
torchvision_resnet50 | Completamente delegato | 6,9 |
torchvision_squeezenet1_0 | Completamente delegato | 2.9 |
torchvision_squeezenet1_1 | Completamente delegato | 2,5 |
torchvision_vgg16 | Completamente delegato | 13,4 |
torchvision_wide_resnet101_2 | Completamente delegato | 25,0 |
torchvision_wide_resnet50_2 | Completamente delegato | 13,4 |
u2net_full | Completamente delegato | 98,3 |
u2net_lite | Completamente delegato | 51,4 |
hf_distil_whisper_small_no_cache | Parzialmente delegato | 251,9 |
hf_distilbert | Parzialmente delegato | 13,7 |
hf_tinyroberta_squad2 | Parzialmente delegato | 17.1 |
hf_tinyroberta_squad2_dynamic_batch | Parzialmente delegato | 52,1 |
snapml_StyleTransferNet | Parzialmente delegato | 40,9 |
timm_efficientformer_l1 | Parzialmente delegato | 17,6 |
timm_efficientformerv2_s0 | Parzialmente delegato | 16.1 |
timm_pvt_v2_b1 | Parzialmente delegato | 73,5 |
timm_pvt_v2_b3 | Parzialmente delegato | 246,7 |
timm_resnest14d | Parzialmente delegato | 88,9 |
torchaudio_conformer | Parzialmente delegato | 21,5 |
torchvision_convnext_tiny | Parzialmente delegato | 8.2 |
torchvision_maxvit_t | Parzialmente delegato | 194,0 |
torchvision_shufflenet_v2 | Parzialmente delegato | 9,5 |
torchvision_swin_tiny | Parzialmente delegato | 164,4 |
torchvision_video_resnet2plus1d_18 | Parzialmente delegato | 6832,0 |
torchvision_video_swin3d_tiny | Parzialmente delegato | 2617,8 |
yolox_tiny | Parzialmente delegato | 11.2 |