GPU-Beschleunigung mit LiteRT

Graphics Processing Units (GPUs) werden aufgrund ihres massiven parallelen Durchsatzes im Vergleich zu CPUs häufig zur Beschleunigung von Deep Learning verwendet. LiteRT vereinfacht die Verwendung der GPU-Beschleunigung, da Nutzer die Hardwarebeschleunigung beim Erstellen eines kompilierten Modells (CompiledModel) als Parameter angeben können.

Mit der GPU-Beschleunigung von LiteRT können Sie GPU-freundliche Ein- und Ausgabepuffer erstellen, Zero-Copy mit Ihren Daten im GPU-Speicher erreichen und Aufgaben asynchron ausführen, um die Parallelität zu maximieren.

Beispielimplementierungen von LiteRT GPU finden Sie in den folgenden Demoanwendungen:

GPU-Abhängigkeit hinzufügen

Führen Sie die folgenden Schritte aus, um Ihrer Kotlin- oder C++-Anwendung eine GPU-Abhängigkeit hinzuzufügen.

Kotlin

Für Kotlin-Nutzer ist der GPU-Accelerator integriert und es sind keine zusätzlichen Schritte über die Kurzanleitung hinaus erforderlich.

C++

C++-Nutzer müssen die Abhängigkeiten der Anwendung mit LiteRT-GPU-Beschleunigung erstellen. Die cc_binary-Regel, die die Kernanwendungslogik verpackt (z.B. main.cc) sind die folgenden Laufzeitkomponenten erforderlich:

  • Gemeinsame Bibliothek der LiteRT C API: Das Attribut data muss die gemeinsame Bibliothek der LiteRT C API (//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 wichtig, da LiteRT unter Android häufig OpenGLES verwendet.
  • Modelldateien und andere Assets: Über das Attribut data eingebunden.

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
)

Bei dieser Einrichtung kann Ihre kompilierte Binärdatei die GPU dynamisch laden und für die beschleunigte Inferenz für maschinelles Lernen verwenden.

GPU mit der CompiledModel API verwenden

Um den GPU-Beschleuniger zu verwenden, übergeben Sie den GPU-Parameter beim Erstellen des kompilierten Modells (CompiledModel). Das folgende Code-Snippet zeigt eine einfache 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.

Zero-Copy mit GPU-Beschleunigung

Durch die Verwendung von Zero-Copy kann eine GPU direkt auf Daten im eigenen Speicher zugreifen, ohne dass die CPU diese Daten explizit kopieren muss. Da keine Daten in den und aus dem CPU-Arbeitsspeicher kopiert werden, kann die End-to-End-Latenz durch Zero-Copy deutlich reduziert werden.

Der folgende Code ist ein Beispiel für die Implementierung von Zero-Copy-GPU mit OpenGL, einer API zum Rendern von Vektorgrafiken. Im Code werden Bilder im OpenGL-Pufferformat direkt an LiteRT übergeben:

// 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, z. B. RunAsync(), können Sie die GPU-Inferenz planen und gleichzeitig andere Aufgaben mit der CPU oder NPU ausführen. In komplexen Pipelines wird die GPU oft asynchron neben der CPU oder NPUs verwendet.

Das folgende Code-Snippet basiert auf dem Code im Beispiel GPU-Beschleunigung ohne Kopieren. Der Code verwendet sowohl CPU als auch GPU asynchron und hängt einen LiteRT-Event an den Eingabepuffer an. LiteRT Event ist für die Verwaltung verschiedener Arten von Synchronisierungsprimitiven zuständig. Mit dem folgenden Code wird ein verwaltetes LiteRT-Event-Objekt vom Typ LiteRtEventTypeEglSyncFence erstellt. Dieses Event-Objekt sorgt dafür, dass wir erst dann aus dem Eingabepuffer lesen, wenn die GPU fertig ist. All dies geschieht ohne Beteiligung 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 unterstützt die GPU-Beschleunigung mit den folgenden Modellen. Benchmark-Ergebnisse basieren auf Tests, die auf einem Samsung Galaxy S24 ausgefü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 2617,8
yolox_tiny Teilweise delegiert 11.2