Procesory graficzne (GPU) są powszechnie używane do przyspieszania uczenia głębokiego ze względu na ich ogromną przepustowość równoległą w porównaniu z procesorami CPU. LiteRT upraszcza proces korzystania z akceleracji GPU, umożliwiając użytkownikom określenie akceleracji sprzętowej jako parametru podczas tworzenia skompilowanego modelu (CompiledModel).
Dzięki akceleracji GPU w LiteRT możesz tworzyć bufory wejściowe i wyjściowe przyjazne dla GPU, osiągać zerowe kopiowanie danych w pamięci GPU i wykonywać zadania asynchronicznie, aby zmaksymalizować równoległość.
Rozpocznij
W przypadku klasycznych modeli ML zapoznaj się z tymi aplikacjami demonstracyjnymi:
- Aplikacja do segmentacji obrazów w Kotlinie: wnioskowanie na CPU/GPU/NPU.
- Aplikacja C++ do segmentacji obrazu: wnioskowanie na CPU/GPU/NPU z wykonywaniem asynchronicznym.
W przypadku modeli generatywnej AI zapoznaj się z tymi demonstracjami i tym przewodnikiem:
- EmbeddingGemma semantic similarity C++ App: wnioskowanie na CPU, GPU i NPU.
- Przewodnik po uruchamianiu LLM za pomocą LiteRT-LM.
Dodawanie zależności GPU
Aby dodać zależność GPU do aplikacji napisanej w Kotlinie lub C++, wykonaj te czynności.
Kotlin
W przypadku użytkowników języka Kotlin akcelerator GPU jest wbudowany i nie wymaga dodatkowych czynności poza tymi opisanymi w przewodniku Pierwsze kroki.
C++
W przypadku użytkowników C++ musisz skompilować zależności aplikacji z akceleracją GPU LiteRT. cc_binary reguła, która pakuje podstawową logikę aplikacji (np. main.cc) wymaga tych komponentów środowiska wykonawczego:
- Biblioteka współdzielona LiteRT C API: atrybut
datamusi zawierać bibliotekę współdzieloną LiteRT C API (//litert/c:litert_runtime_c_api_shared_lib) i komponenty specyficzne dla procesora graficznego (litert_gpu_accelerator_prebuilts). - Zależności atrybutów: atrybut
depszwykle zawiera zależności GLESgles_deps(), a atrybutlinkoptszwykle zawieragles_linkopts(). Oba te interfejsy są bardzo istotne w przypadku akceleracji za pomocą procesora graficznego, ponieważ LiteRT często używa OpenGLES na Androidzie. - Pliki modelu i inne komponenty: uwzględnione za pomocą atrybutu
data.
Oto przykład reguły 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
)
Ta konfiguracja umożliwia skompilowanemu plikowi binarnemu dynamiczne wczytywanie i używanie procesora graficznego do przyspieszonego wnioskowania w uczeniu maszynowym.
Gotowe akceleratory GPU
Nowy akcelerator GPU LiteRT nie jest jeszcze dostępny na licencji open source. Ale gotowe zestawy są dostępne. W przypadku użytkowników języka Kotlin pakiet Maven LiteRT zawiera już akceleratory GPU. Użytkownicy pakietu SDK C++ muszą pobrać go osobno, korzystając z tego.
W Bazelu możesz użyć tej reguły, aby dodać zależność do elementu docelowego.
cpp
load("//litert/build_common:special_rule.bzl", "litert_gpu_accelerator_prebuilts")
Korzystanie z GPU w interfejsie API CompiledModel
Aby zacząć korzystać z akceleratora GPU, podczas tworzenia skompilowanego modelu (CompiledModel) przekaż parametr GPU. Ten fragment kodu pokazuje podstawową implementację całego procesu:
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()
Więcej informacji znajdziesz w przewodnikach Pierwsze kroki z C++ i Pierwsze kroki z Kotlinem.
Kopiowanie zerowe z akceleracją GPU
Używanie zerowego kopiowania umożliwia procesorowi graficznemu bezpośredni dostęp do danych w jego własnej pamięci bez konieczności jawnego kopiowania tych danych przez procesor. Dzięki temu, że nie trzeba kopiować danych do pamięci procesora ani z niej, zero-copy może znacznie skrócić opóźnienie od początku do końca.
Poniższy kod to przykładowa implementacja Zero-Copy GPU z OpenGL, interfejsem API do renderowania grafiki wektorowej. Kod przekazuje obrazy w formacie bufora OpenGL bezpośrednio do 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());
Asynchroniczne wykonywanie
Asynchroniczne metody LiteRT, takie jak RunAsync(), umożliwiają planowanie wnioskowania na GPU podczas wykonywania innych zadań na CPU lub NPU. W złożonych potokach procesor graficzny jest często używany asynchronicznie wraz z procesorem lub jednostkami NPU.
Ten fragment kodu jest oparty na kodzie podanym w przykładzie Akceleracja GPU bez kopiowania. Kod używa procesora i procesora graficznego asynchronicznie i dołącza do bufora wejściowego LiteRT Event. LiteRT Event
odpowiada za zarządzanie różnymi typami elementów synchronizacji, a poniższy kod tworzy zarządzany obiekt zdarzenia LiteRT typu
LiteRtEventTypeEglSyncFence. Ten obiekt Event zapewnia, że nie będziemy odczytywać danych z bufora wejściowego, dopóki GPU nie zakończy pracy. Wszystko to odbywa się bez udziału procesora.
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));
Obsługiwany backend
LiteRT obsługuje te back-endy GPU na poszczególnych platformach:
| Platforma | Backend |
|---|---|
| Android | OpenCL + OpenGL |
| Linux | WebGPU (Vulkan) |
| macOS | Metal |
| Windows | WebGPU (Direct3D) |
| Android | OpenCL + OpenGL |
Obsługiwane modele
LiteRT obsługuje akcelerację GPU w przypadku tych modeli: Wyniki testów porównawczych są oparte na testach przeprowadzonych na urządzeniu Samsung Galaxy S24.
| Model | Akceleracja GPU LiteRT | GPU LiteRT (ms) |
|---|---|---|
| hf_mms_300m | W pełni przekazane | 19,6 |
| hf_mobilevit_small | W pełni przekazane | 8.7 |
| hf_mobilevit_small_e2e | W pełni przekazane | z Androidem 8.0 |
| hf_wav2vec2_base_960h | W pełni przekazane | 9.1 |
| hf_wav2vec2_base_960h_dynamic | W pełni przekazane | 9.8 |
| isnet | W pełni przekazane | 43,1 |
| timm_efficientnet | W pełni przekazane | 3,7 |
| timm_nfnet | W pełni przekazane | 9.7 |
| timm_regnety_120 | W pełni przekazane | 12.1 |
| torchaudio_deepspeech | W pełni przekazane | 4,6 |
| torchaudio_wav2letter | W pełni przekazane | 4.8 |
| torchvision_alexnet | W pełni przekazane | 3.3 |
| torchvision_deeplabv3_mobilenet_v3_large | W pełni przekazane | 5.7 |
| torchvision_deeplabv3_resnet101 | W pełni przekazane | 35,1 |
| torchvision_deeplabv3_resnet50 | W pełni przekazane | 24,5 |
| torchvision_densenet121 | W pełni przekazane | 13,9 |
| torchvision_efficientnet_b0 | W pełni przekazane | 3.6 |
| torchvision_efficientnet_b1 | W pełni przekazane | 4,7 |
| torchvision_efficientnet_b2 | W pełni przekazane | 5,0 |
| torchvision_efficientnet_b3 | W pełni przekazane | 6.1 |
| torchvision_efficientnet_b4 | W pełni przekazane | 7.6 |
| torchvision_efficientnet_b5 | W pełni przekazane | 8.6 |
| torchvision_efficientnet_b6 | W pełni przekazane | 11.2 |
| torchvision_efficientnet_b7 | W pełni przekazane | 14.7 |
| torchvision_fcn_resnet50 | W pełni przekazane | 19,9 |
| torchvision_googlenet | W pełni przekazane | 3,9 |
| torchvision_inception_v3 | W pełni przekazane | 8.6 |
| torchvision_lraspp_mobilenet_v3_large | W pełni przekazane | 3.3 |
| torchvision_mnasnet0_5 | W pełni przekazane | 2.4 |
| torchvision_mobilenet_v2 | W pełni przekazane | 2.8 |
| torchvision_mobilenet_v3_large | W pełni przekazane | 2.8 |
| torchvision_mobilenet_v3_small | W pełni przekazane | 2.3 |
| torchvision_resnet152 | W pełni przekazane | 15,0 |
| torchvision_resnet18 | W pełni przekazane | 4,3 |
| torchvision_resnet50 | W pełni przekazane | 6.9 |
| torchvision_squeezenet1_0 | W pełni przekazane | 2.9 |
| torchvision_squeezenet1_1 | W pełni przekazane | 2,5 |
| torchvision_vgg16 | W pełni przekazane | 13.4 |
| torchvision_wide_resnet101_2 | W pełni przekazane | 25,0 |
| torchvision_wide_resnet50_2 | W pełni przekazane | 13.4 |
| u2net_full | W pełni przekazane | 98,3 |
| u2net_lite | W pełni przekazane | 51,4 |
| hf_distil_whisper_small_no_cache | Częściowo przekazane | 251,90 EGP |
| hf_distilbert | Częściowo przekazane | 13.7 |
| hf_tinyroberta_squad2 | Częściowo przekazane | 17.1 |
| hf_tinyroberta_squad2_dynamic_batch | Częściowo przekazane | 52,1 |
| snapml_StyleTransferNet | Częściowo przekazane | 40,9 |
| timm_efficientformer_l1 | Częściowo przekazane | 17,6 |
| timm_efficientformerv2_s0 | Częściowo przekazane | 16.1 |
| timm_pvt_v2_b1 | Częściowo przekazane | 73,5 |
| timm_pvt_v2_b3 | Częściowo przekazane | 246,70 EGP |
| timm_resnest14d | Częściowo przekazane | 88,9 |
| torchaudio_conformer | Częściowo przekazane | 21,5 |
| torchvision_convnext_tiny | Częściowo przekazane | 8.2 |
| torchvision_maxvit_t | Częściowo przekazane | 194,0 |
| torchvision_shufflenet_v2 | Częściowo przekazane | 9.5 |
| torchvision_swin_tiny | Częściowo przekazane | 164,4 |
| torchvision_video_resnet2plus1d_18 | Częściowo przekazane | 6832,0 |
| torchvision_video_swin3d_tiny | Częściowo przekazane | 2617,80 EGP |
| yolox_tiny | Częściowo przekazane | 11.2 |