Ускорение графического процессора с помощью LiteRT

Графические процессоры (GPU) широко используются для ускорения глубокого обучения благодаря их огромной параллельной пропускной способности по сравнению с центральными процессорами (CPU). LiteRT упрощает процесс использования аппаратного ускорения GPU, позволяя пользователям указывать аппаратное ускорение в качестве параметра при создании скомпилированной модели ( CompiledModel ).

Благодаря ускорению на графическом процессоре в LiteRT вы можете создавать удобные для графического процессора входные и выходные буферы, обеспечивать копирование данных в памяти графического процессора без копирования и выполнять задачи асинхронно для максимального параллелизма.

Примеры реализации LiteRT GPU можно найти в следующих демонстрационных приложениях:

Добавить зависимость от графического процессора.

Выполните следующие шаги, чтобы добавить зависимость от графического процессора в ваше приложение на Kotlin или C++.

Котлин

Для пользователей Kotlin графический ускоритель встроен и не требует дополнительных действий, помимо тех, что описаны в руководстве по началу работы .

C++

Пользователям C++ необходимо собрать зависимости приложения с использованием ускорения LiteRT GPU. Правило cc_binary , которое упаковывает основную логику приложения (например, main.cc ), требует наличия следующих компонентов среды выполнения:

  • Библиотека LiteRT C API : атрибут data должен включать библиотеку LiteRT C API ( //litert/c:litert_runtime_c_api_shared_lib ) и компоненты, специфичные для графического процессора ( @litert_gpu//:jni/arm64-v8a/libLiteRtGpuAccelerator.so ).
  • Атрибут dependencies : Атрибут deps обычно включает зависимости GLES gles_deps() , а linkopts обычно включает gles_linkopts() . Оба атрибута очень важны для ускорения работы GPU, поскольку LiteRT часто использует OpenGLES на Android.
  • Файлы моделей и другие ресурсы : включаются посредством атрибута data .

Ниже приведён пример правила 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
)

Такая настройка позволяет скомпилированному исполняемому файлу динамически загружать и использовать графический процессор для ускорения выполнения вычислений в рамках машинного обучения.

Использование графического процессора с API CompiledModel

Для начала использования графического ускорителя передайте параметр GPU при создании скомпилированной модели ( CompiledModel ). Следующий фрагмент кода демонстрирует базовую реализацию всего процесса:

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));

Котлин

// 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()

Для получения более подробной информации см. руководства « Начало работы с C++» или «Начало работы с Kotlin» .

Копирование без копирования с ускорением на графическом процессоре

Использование технологии «нулевого копирования» позволяет графическому процессору получать доступ к данным непосредственно в собственной памяти без необходимости явного копирования этих данных центральным процессором. Благодаря отсутствию копирования данных в память центрального процессора и из неё, технология «нулевого копирования» может значительно снизить сквозную задержку.

Приведённый ниже код представляет собой пример реализации технологии Zero-Copy GPU с использованием OpenGL , API для рендеринга векторной графики. Код передаёт изображения в формате буфера OpenGL непосредственно в 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 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());

Асинхронное выполнение

Асинхронные методы LiteRT, такие как RunAsync() , позволяют планировать выполнение задач на графическом процессоре, продолжая при этом другие задачи на центральном процессоре или нейронном процессоре. В сложных конвейерах графический процессор часто используется асинхронно наряду с центральным процессором или нейронным процессором.

Приведенный ниже фрагмент кода основан на коде, представленном в примере ускорения GPU без копирования . Код использует как CPU, так и GPU асинхронно и прикрепляет Event LiteRT к входному буферу. Event LiteRT отвечает за управление различными типами примитивов синхронизации, и следующий код создает управляемый объект события LiteRT типа LiteRtEventTypeEglSyncFence . Этот объект Event гарантирует, что чтение из входного буфера не будет производиться до завершения работы GPU. Все это выполняется без участия 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));

Поддерживаемые модели

LiteRT поддерживает ускорение графического процессора на следующих моделях. Результаты бенчмарков основаны на тестах, проведенных на устройстве Samsung Galaxy S24.

Модель Ускорение LiteRT на графическом процессоре LiteRT GPU (мс)
hf_mms_300m Полностью делегировано 19.6
hf_mobilevit_small Полностью делегировано 8.7
hf_mobilevit_small_e2e Полностью делегировано 8.0
hf_wav2vec2_base_960h Полностью делегировано 9.1
hf_wav2vec2_base_960h_dynamic Полностью делегировано 9.8
интернет Полностью делегировано 43.1
timm_efficientnet Полностью делегировано 3.7
timm_nfnet Полностью делегировано 9.7
timm_regnety_120 Полностью делегировано 12.1
torchaudio_deepspeech Полностью делегировано 4.6
torchaudio_wav2letter Полностью делегировано 4.8
torchvision_alexnet Полностью делегировано 3.3
torchvision_deeplabv3_mobilenet_v3_large Полностью делегировано 5.7
torchvision_deeplabv3_resnet101 Полностью делегировано 35.1
torchvision_deeplabv3_resnet50 Полностью делегировано 24.5
torchvision_densenet121 Полностью делегировано 13.9
torchvision_efficientnet_b0 Полностью делегировано 3.6
torchvision_efficientnet_b1 Полностью делегировано 4.7
torchvision_efficientnet_b2 Полностью делегировано 5.0
torchvision_efficientnet_b3 Полностью делегировано 6.1
torchvision_efficientnet_b4 Полностью делегировано 7.6
torchvision_efficientnet_b5 Полностью делегировано 8.6
torchvision_efficientnet_b6 Полностью делегировано 11.2
torchvision_efficientnet_b7 Полностью делегировано 14.7
torchvision_fcn_resnet50 Полностью делегировано 19.9
torchvision_googlenet Полностью делегировано 3.9
torchvision_inception_v3 Полностью делегировано 8.6
torchvision_lraspp_mobilenet_v3_large Полностью делегировано 3.3
torchvision_mnasnet0_5 Полностью делегировано 2.4
torchvision_mobilenet_v2 Полностью делегировано 2.8
torchvision_mobilenet_v3_large Полностью делегировано 2.8
torchvision_mobilenet_v3_small Полностью делегировано 2.3
torchvision_resnet152 Полностью делегировано 15.0
torchvision_resnet18 Полностью делегировано 4.3
torchvision_resnet50 Полностью делегировано 6.9
torchvision_squeezenet1_0 Полностью делегировано 2.9
torchvision_squeezenet1_1 Полностью делегировано 2.5
torchvision_vgg16 Полностью делегировано 13.4
torchvision_wide_resnet101_2 Полностью делегировано 25.0
torchvision_wide_resnet50_2 Полностью делегировано 13.4
u2net_full Полностью делегировано 98.3
u2net_lite Полностью делегировано 51.4
hf_distil_whisper_small_no_cache Частично делегированный 251.9
hf_distilbert Частично делегированный 13.7
hf_tinyroberta_squad2 Частично делегированный 17.1
hf_tinyroberta_squad2_dynamic_batch Частично делегированный 52.1
snapml_StyleTransferNet Частично делегированный 40.9
timm_efficientformer_l1 Частично делегированный 17.6
timm_efficientformerv2_s0 Частично делегированный 16.1
timm_pvt_v2_b1 Частично делегированный 73.5
timm_pvt_v2_b3 Частично делегированный 246.7
timm_resnest14d Частично делегированный 88.9
torchaudio_conformer Частично делегированный 21.5
torchvision_convnext_tiny Частично делегированный 8.2
torchvision_maxvit_t Частично делегированный 194.0
torchvision_shufflenet_v2 Частично делегированный 9.5
torchvision_swin_tiny Частично делегированный 164.4
torchvision_video_resnet2plus1d_18 Частично делегированный 6832.0
torchvision_video_swin3d_tiny Частично делегированный 2617.8
yolox_tiny Частично делегированный 11.2