GPU-Beschleunigung mit LiteRT Next

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ängigkeiten gles_deps() und linkopts enthält in der Regel gles_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