Grafikprozessoren (GPUs) werden aufgrund ihres enormen parallelen Durchsatzes im Vergleich zu CPUs häufig für die Beschleunigung von Deep Learning verwendet. LiteRT Next vereinfacht die Verwendung der GPU-Beschleunigung, da Nutzer die Hardwarebeschleunigung beim Erstellen eines kompilierten Modells (CompiledModel
) als Parameter angeben können. LiteRT Next verwendet außerdem eine neue und verbesserte GPU-Beschleunigungsimplementierung, die von LiteRT nicht angeboten wird.
Mit der GPU-Beschleunigung von LiteRT Next können Sie GPU-freundliche Eingabe- und Ausgabe-Buffer erstellen, Zero-Copy-Datenübertragungen im GPU-Speicher erzielen und Aufgaben asynchron ausführen, um die Parallelität zu maximieren.
Beispielimplementierungen von LiteRT Next mit GPU-Unterstützung finden Sie in den folgenden Demoanwendungen:
GPU-Abhängigkeit hinzufügen
So fügen Sie Ihrer Kotlin- oder C++-Anwendung eine GPU-Abhängigkeit hinzu:
Kotlin
Für Kotlin-Nutzer ist der GPU-Accelerator bereits integriert und es sind keine zusätzlichen Schritte erforderlich, die über die Einstiegsanleitung hinausgehen.
C++
C++-Nutzer müssen die Abhängigkeiten der Anwendung mit der LiteRT-GPU-Beschleunigung erstellen. Die cc_binary
-Regel, die die Hauptanwendungslogik enthält (z.B. main.cc
) sind die folgenden Laufzeitkomponenten erforderlich:
- LiteRT C API-Bibliothek: Das Attribut
data
muss die LiteRT C API-Bibliothek (//litert/c:litert_runtime_c_api_shared_lib
) und GPU-spezifische Komponenten (@litert_gpu//:jni/arm64-v8a/libLiteRtGpuAccelerator.so
) enthalten. - Attributabhängigkeiten: Das Attribut
deps
enthält in der Regel GLES-Abhängigkeitengles_deps()
undlinkopts
enthält in der Regelgles_linkopts()
. Beide sind für die GPU-Beschleunigung sehr relevant, da LiteRT unter Android häufig OpenGLES verwendet. - Modelldateien und andere Assets: Über das Attribut
data
eingeschlossen.
Das folgende Beispiel zeigt eine cc_binary
-Regel:
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
)
Mit dieser Einrichtung kann die GPU für die beschleunigte Inferenz beim maschinellen Lernen dynamisch geladen und verwendet werden.
Jetzt starten
Wenn Sie den GPU-Beschleuniger verwenden möchten, geben Sie den GPU-Parameter beim Erstellen des kompilierten Modells (CompiledModel
) an. Das folgende Code-Snippet zeigt eine grundlegende Implementierung des gesamten Prozesses:
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()
Weitere Informationen finden Sie in den Anleitungen Erste Schritte mit C++ oder Erste Schritte mit Kotlin.
LiteRT Next GPU Accelerator
Der neue GPU-Accelerator, der nur mit LiteRT Next verfügbar ist, ist für die effizientere Verarbeitung von KI-Arbeitslasten wie großen Matrixmultiplikationen und KV-Cache für LLMs optimiert als frühere Versionen. Der LiteRT Next-GPU-Beschleuniger bietet im Vergleich zur LiteRT-Version folgende wichtige Verbesserungen:
- Erweiterte Abdeckung von Mobilfunkanbietern:Unterstützung größerer, komplexerer neuronaler Netzwerke.
- Verbesserte Interoperabilität von Buffers:GPU-Buffers können jetzt direkt für Kameraframes, 2D-Texturen oder große LLM-Zustände verwendet werden.
- Unterstützung für die asynchrone Ausführung:Überlappen Sie die CPU-Vorverarbeitung mit der GPU-Inferenz.
Zero-Copy mit GPU-Beschleunigung
Mit Zero-Copy kann eine GPU direkt auf Daten in ihrem eigenen Arbeitsspeicher zugreifen, ohne dass die CPU diese Daten explizit kopieren muss. Da bei der Zero-Copy-Technologie keine Daten in den und aus dem CPU-Speicher kopiert werden, kann die End-to-End-Latenz deutlich reduziert werden.
Der folgende Code ist eine Beispielimplementierung der Zero-Copy-GPU mit OpenGL, einer API zum Rendern von Vektorgrafiken. Der Code übergibt Bilder im OpenGL-Pufferformat direkt an 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());
Asynchrone Ausführung
Mit den asynchronen Methoden von LiteRT wie RunAsync()
können Sie GPU-Inferenzen planen und gleichzeitig andere Aufgaben mit der CPU oder der NPU ausführen. In komplexen Pipelines werden GPUs oft asynchron neben CPUs oder NPUs verwendet.
Das folgende Code-Snippet basiert auf dem Code im Beispiel GPU-Beschleunigung ohne Kopiervorgang. Der Code verwendet sowohl die CPU als auch die GPU asynchron und hängt dem Eingabebuffer eine LiteRT-Event
an. LiteRT Event
ist für die Verwaltung verschiedener Arten von Synchronisierungsprimitiven verantwortlich. Mit dem folgenden Code wird ein verwaltetes LiteRT-Ereignisobjekt vom Typ LiteRtEventTypeEglSyncFence
erstellt. Dieses Event
-Objekt sorgt dafür, dass erst gelesen wird, wenn die GPU fertig ist. All dies geschieht ohne Einbindung der 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));
Unterstützte Modelle
LiteRT Next unterstützt die GPU-Beschleunigung mit den folgenden Modellen. Die Benchmark-Ergebnisse basieren auf Tests, die auf einem Samsung Galaxy S24 durchgeführt wurden.
Modell | LiteRT-GPU-Beschleunigung | LiteRT GPU (ms) |
---|---|---|
hf_mms_300m | Vollständig delegiert | 19,6 |
hf_mobilevit_small | Vollständig delegiert | 8.7 |
hf_mobilevit_small_e2e | Vollständig delegiert | 8.0 |
hf_wav2vec2_base_960h | Vollständig delegiert | 9,1 |
hf_wav2vec2_base_960h_dynamic | Vollständig delegiert | 9,8 |
isnet | Vollständig delegiert | 43.1 |
timm_efficientnet | Vollständig delegiert | 3,7 |
timm_nfnet | Vollständig delegiert | 9.7 |
timm_regnety_120 | Vollständig delegiert | 12.1 |
torchaudio_deepspeech | Vollständig delegiert | 4,6 |
torchaudio_wav2letter | Vollständig delegiert | 4,8 |
torchvision_alexnet | Vollständig delegiert | 3.3 |
torchvision_deeplabv3_mobilenet_v3_large | Vollständig delegiert | 5.7 |
torchvision_deeplabv3_resnet101 | Vollständig delegiert | 35,1 |
torchvision_deeplabv3_resnet50 | Vollständig delegiert | 24,5 |
torchvision_densenet121 | Vollständig delegiert | 13.9 |
torchvision_efficientnet_b0 | Vollständig delegiert | 3,6 |
torchvision_efficientnet_b1 | Vollständig delegiert | 4,7 |
torchvision_efficientnet_b2 | Vollständig delegiert | 5 |
torchvision_efficientnet_b3 | Vollständig delegiert | 6.1 |
torchvision_efficientnet_b4 | Vollständig delegiert | 7.6 |
torchvision_efficientnet_b5 | Vollständig delegiert | 8.6 |
torchvision_efficientnet_b6 | Vollständig delegiert | 11.2 |
torchvision_efficientnet_b7 | Vollständig delegiert | 14.7 |
torchvision_fcn_resnet50 | Vollständig delegiert | 19,9 |
torchvision_googlenet | Vollständig delegiert | 3,9 |
torchvision_inception_v3 | Vollständig delegiert | 8.6 |
torchvision_lraspp_mobilenet_v3_large | Vollständig delegiert | 3.3 |
torchvision_mnasnet0_5 | Vollständig delegiert | 2.4 |
torchvision_mobilenet_v2 | Vollständig delegiert | 2,8 |
torchvision_mobilenet_v3_large | Vollständig delegiert | 2,8 |
torchvision_mobilenet_v3_small | Vollständig delegiert | 2.3 |
torchvision_resnet152 | Vollständig delegiert | 15.0 |
torchvision_resnet18 | Vollständig delegiert | 4,3 |
torchvision_resnet50 | Vollständig delegiert | 6.9 |
torchvision_squeezenet1_0 | Vollständig delegiert | 2,9 |
torchvision_squeezenet1_1 | Vollständig delegiert | 2,5 |
torchvision_vgg16 | Vollständig delegiert | 13,4 |
torchvision_wide_resnet101_2 | Vollständig delegiert | 25.0 |
torchvision_wide_resnet50_2 | Vollständig delegiert | 13,4 |
u2net_full | Vollständig delegiert | 98,3 |
u2net_lite | Vollständig delegiert | 51,4 |
hf_distil_whisper_small_no_cache | Teilweise delegiert | 251,9 |
hf_distilbert | Teilweise delegiert | 13.7 |
hf_tinyroberta_squad2 | Teilweise delegiert | 17,1 |
hf_tinyroberta_squad2_dynamic_batch | Teilweise delegiert | 52,1 |
snapml_StyleTransferNet | Teilweise delegiert | 40,9 |
timm_efficientformer_l1 | Teilweise delegiert | 17,6 |
timm_efficientformerv2_s0 | Teilweise delegiert | 16.1 |
timm_pvt_v2_b1 | Teilweise delegiert | 73,5 |
timm_pvt_v2_b3 | Teilweise delegiert | 246,7 |
timm_resnest14d | Teilweise delegiert | 88,9 |
torchaudio_conformer | Teilweise delegiert | 21.5 |
torchvision_convnext_tiny | Teilweise delegiert | 8.2 |
torchvision_maxvit_t | Teilweise delegiert | 194,0 |
torchvision_shufflenet_v2 | Teilweise delegiert | 9,5 |
torchvision_swin_tiny | Teilweise delegiert | 164,4 |
torchvision_video_resnet2plus1d_18 | Teilweise delegiert | 6832.0 |
torchvision_video_swin3d_tiny | Teilweise delegiert | 2.617,8 |
yolox_tiny | Teilweise delegiert | 11.2 |