グラフィック プロセッシング ユニット(GPU)は、CPU と比較して並列スループットが大きいため、ディープ ラーニングの高速化によく使用されます。LiteRT は、コンパイル済みモデル(CompiledModel)の作成時にハードウェア アクセラレーションをパラメータとして指定できるようにすることで、GPU アクセラレーションの使用プロセスを簡素化します。
LiteRT の GPU アクセラレーションを使用すると、GPU に適した入力バッファと出力バッファを作成し、GPU メモリ内のデータでゼロコピーを実現し、タスクを非同期で実行して並列処理を最大化できます。
LiteRT GPU の実装例については、次のデモアプリを参照してください。
GPU の依存関係を追加する
次の手順に沿って、Kotlin または C++ アプリケーションに GPU 依存関係を追加します。
Kotlin
Kotlin ユーザーの場合、GPU アクセラレータは組み込みであり、スタートガイドの手順以外に特別な手順は必要ありません。
C++
C++ ユーザーは、LiteRT GPU アクセラレーションを使用してアプリケーションの依存関係をビルドする必要があります。コア アプリケーション ロジック(cc_binarymain.cc)には、次のランタイム コンポーネントが必要です。
- LiteRT C API 共有ライブラリ:
data属性には、LiteRT C API 共有ライブラリ(//litert/c:litert_runtime_c_api_shared_lib)と GPU 固有のコンポーネント(@litert_gpu//:jni/arm64-v8a/libLiteRtGpuAccelerator.so)を含める必要があります。 - 属性の依存関係:
deps属性には通常 GLES 依存関係gles_deps()が含まれ、linkoptsには通常gles_linkopts()が含まれます。LiteRT は Android で OpenGLES を使用することが多いため、どちらも GPU アクセラレーションに非常に適しています。 - モデルファイルとその他のアセット:
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
)
この設定により、コンパイルされたバイナリが GPU を動的に読み込んで使用し、ML 推論を高速化できます。
CompiledModel API で GPU を使用する
GPU アクセラレータの使用を開始するには、コンパイル済みモデル(CompiledModel)の作成時に GPU パラメータを渡します。次のコード スニペットは、プロセス全体の基本的な実装を示しています。
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()
詳細については、C++ スタートガイドまたは Kotlin スタートガイドをご覧ください。
GPU アクセラレーションによるゼロコピー
ゼロコピーを使用すると、CPU がデータを明示的にコピーしなくても、GPU が独自のメモリ内のデータに直接アクセスできます。ゼロコピーでは、CPU メモリとの間でデータをコピーしないため、エンドツーエンドのレイテンシを大幅に短縮できます。
次のコードは、ベクトル グラフィックのレンダリング用 API である OpenGL を使用したゼロコピー GPU の実装例です。コードは、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() など)を使用すると、CPU または NPU を使用して他のタスクを継続しながら、GPU 推論をスケジュールできます。複雑なパイプラインでは、GPU は CPU や NPU とともに非同期で使用されることがよくあります。
次のコード スニペットは、ゼロコピー GPU アクセラレーションの例で提供されているコードを基にしています。このコードは CPU と GPU の両方を非同期で使用し、入力バッファに LiteRT Event を接続します。LiteRT Event は、さまざまなタイプの同期プリミティブを管理します。次のコードは、LiteRtEventTypeEglSyncFence タイプのマネージド LiteRT イベント オブジェクトを作成します。この 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 は、次のモデルで GPU アクセラレーションをサポートしています。ベンチマークの結果は、Samsung Galaxy S24 デバイスで実施したテストに基づいています。