LiteRT CompiledModel C++ API

LiteRT CompiledModel API 以 C++ 提供,可让开发者对内存分配和低阶开发进行精细控制。如需查看示例,请参阅图像分割 C++ 应用

以下指南展示了 CompiledModel Kotlin API 的基本 CPU 推理。如需了解高级加速功能,请参阅有关 GPU 加速NPU 加速的指南。

添加 build 依赖项

选择适合您项目的路径:

基本推理

本部分介绍如何执行基本推理。

创建环境

Environment 对象提供了一个运行时环境,其中包含编译器插件的路径和 GPU 上下文等组件。创建 CompiledModelTensorBuffer 时必须提供 Environment。以下代码创建了一个 Environment,用于在 CPU 和 GPU 上执行,不带任何选项:

LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));

创建 CompiledModel

获得 LiteRT 模型或将模型转换为 .tflite 格式后,使用 CompiledModel API 通过模型文件初始化运行时。您可以在此点指定硬件加速(kLiteRtHwAcceleratorCpukLiteRtHwAcceleratorGpu):

LITERT_ASSIGN_OR_RETURN(auto compiled_model,
  CompiledModel::Create(env, model, kLiteRtHwAcceleratorCpu));

创建输入和输出缓冲区

创建必要的数据结构(缓冲区),以保存您将输入到模型中进行推理的输入数据,以及模型在运行推理后生成的输出数据。

LITERT_ASSIGN_OR_RETURN(auto input_buffers, compiled_model.CreateInputBuffers());
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());

如果您使用的是 CPU 内存,请通过将数据直接写入第一个输入缓冲区来填充输入。

input_buffers[0].Write<float>(absl::MakeConstSpan(input_data, input_size));

调用模型

提供输入和输出缓冲区,并使用上一步中指定的模型和硬件加速来运行编译后的模型。

compiled_model.Run(input_buffers, output_buffers);

检索输出

通过直接从内存中读取模型输出来检索输出。

std::vector<float> data(output_data_size);
output_buffers[0].Read<float>(absl::MakeSpan(data));
// ... process output data

主要概念和组件

如需了解 LiteRT CompiledModel API 的主要概念和组件,请参阅以下部分。

错误处理

LiteRT 使用 litert::Expected 返回值或传播错误,方式与 absl::StatusOrstd::expected 类似。您可以自行手动检查是否存在此错误。

为方便起见,LiteRT 提供了以下宏:

  • 如果 expr 未产生错误,LITERT_ASSIGN_OR_RETURN(lhs, expr) 会将 expr 的结果分配给 lhs,否则会返回错误。

    它会展开为类似以下代码段的内容。

    auto maybe_model = CompiledModel::Create(env, "mymodel.tflite", HwAccelerators::kCpu);
    if (!maybe_model) {
      return maybe_model.Error();
    }
    auto model = std::move(maybe_model.Value());
    
  • LITERT_ASSIGN_OR_ABORT(lhs, expr)LITERT_ASSIGN_OR_RETURN 执行的操作相同,但会在出现错误时中止程序。

  • 如果 LITERT_RETURN_IF_ERROR(expr) 的评估结果为错误,则返回 expr

  • LITERT_ABORT_IF_ERROR(expr) 的作用与 LITERT_RETURN_IF_ERROR 相同,但如果出现错误,则会中止程序。

如需详细了解 LiteRT 宏,请参阅 litert_macros.h

Tensor 缓冲区 (TensorBuffer)

LiteRT 使用 Tensor Buffer API (TensorBuffer) 来处理编译后模型的数据流入和流出,从而为 I/O 缓冲区互操作性提供内置支持。Tensor Buffer API 提供写入 (Write<T>()) 和读取 (Read<T>()) 以及锁定 CPU 内存的功能。

如需更全面地了解 TensorBuffer API 的实现方式,请参阅 litert_tensor_buffer.h 的源代码。

查询模型输入/输出要求

分配张量缓冲区 (TensorBuffer) 的要求通常由硬件加速器指定。输入和输出的缓冲区在对齐、缓冲区步长和内存类型方面可能存在要求。您可以使用 CreateInputBuffers 等辅助函数来自动处理这些要求。

以下简化的代码段演示了如何检索输入数据的缓冲区要求:

LITERT_ASSIGN_OR_RETURN(auto reqs, compiled_model.GetInputBufferRequirements(signature_index, input_index));

如需更全面地了解 TensorBufferRequirements API 的实现方式,请参阅 litert_tensor_buffer_requirements.h 的源代码。

创建受管理的 Tensor 缓冲区 (TensorBuffers)

以下简化的代码段演示了如何创建受管理的张量缓冲区,其中 TensorBuffer API 会分配相应的缓冲区:

LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_cpu,
TensorBuffer::CreateManaged(env, /*buffer_type=*/kLiteRtTensorBufferTypeHostMemory,
  ranked_tensor_type, buffer_size));

LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_gl, TensorBuffer::CreateManaged(env,
  /*buffer_type=*/kLiteRtTensorBufferTypeGlBuffer, ranked_tensor_type, buffer_size));

LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_ahwb, TensorBuffer::CreateManaged(env,
  /*buffer_type=*/kLiteRtTensorBufferTypeAhwb, ranked_tensor_type, buffer_size));

创建零复制张量缓冲区

如需将现有缓冲区封装为 Tensor 缓冲区(零复制),请使用以下代码段:

// Create a TensorBuffer from host memory
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_from_host,
  TensorBuffer::CreateFromHostMemory(env, ranked_tensor_type,
  ptr_to_host_memory, buffer_size));

// Create a TensorBuffer from GlBuffer
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_from_gl,
  TensorBuffer::CreateFromGlBuffer(env, ranked_tensor_type, gl_target, gl_id,
  size_bytes, offset));

// Create a TensorBuffer from AHardware Buffer
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_from_ahwb,
  TensorBuffer::CreateFromAhwb(env, ranked_tensor_type, ahardware_buffer, offset));

从 Tensor 缓冲区读取和写入

以下代码段演示了如何从输入缓冲区读取数据并写入输出缓冲区:

// Example of reading to input buffer:
std::vector<float> input_tensor_data = {1,2};
LITERT_ASSIGN_OR_RETURN(auto write_success,
  input_tensor_buffer.Write<float>(absl::MakeConstSpan(input_tensor_data)));
if(write_success){
  /* Continue after successful write... */
}

// Example of writing to output buffer:
std::vector<float> data(total_elements);
LITERT_ASSIGN_OR_RETURN(auto read_success,
  output_tensor_buffer.Read<float>(absl::MakeSpan(data)));
if(read_success){
  /* Continue after successful read */
}

高级:针对专用硬件缓冲区类型的零复制缓冲区互操作

某些缓冲区类型(例如 AHardwareBuffer)允许与其他缓冲区类型进行互操作。例如,可以使用零复制从 AHardwareBuffer 创建 OpenGL 缓冲区。以下代码段展示了一个示例:

LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_ahwb,
  TensorBuffer::CreateManaged(env, kLiteRtTensorBufferTypeAhwb,
  ranked_tensor_type, buffer_size));
// Buffer interop: Get OpenGL buffer from AHWB,
// internally creating an OpenGL buffer backed by AHWB memory.
LITERT_ASSIGN_OR_RETURN(auto gl_buffer, tensor_buffer_ahwb.GetGlBuffer());

还可以从 AHardwareBuffer 创建 OpenCL 缓冲区:

LITERT_ASSIGN_OR_RETURN(auto cl_buffer, tensor_buffer_ahwb.GetOpenClMemory());

在支持 OpenCL 和 OpenGL 之间互操作性的移动设备上,可以从 GL 缓冲区创建 CL 缓冲区:

LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_from_gl,
  TensorBuffer::CreateFromGlBuffer(env, ranked_tensor_type, gl_target, gl_id,
  size_bytes, offset));

// Creates an OpenCL buffer from the OpenGL buffer, zero-copy.
LITERT_ASSIGN_OR_RETURN(auto cl_buffer, tensor_buffer_from_gl.GetOpenClMemory());

实现示例

请参阅以下 LiteRT 在 C++ 中的实现。

基本推理(CPU)

以下是开始使用部分中的代码段的精简版本。这是使用 LiteRT 进行推理的最简单实现。

// Load model and initialize runtime
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create({}));
LITERT_ASSIGN_OR_RETURN(auto compiled_model, CompiledModel::Create(env, "mymodel.tflite",
  kLiteRtHwAcceleratorCpu));

// Preallocate input/output buffers
LITERT_ASSIGN_OR_RETURN(auto input_buffers, compiled_model.CreateInputBuffers());
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());

// Fill the first input
float input_values[] = { /* your data */ };
input_buffers[0].Write<float>(absl::MakeConstSpan(input_values, /*size*/));

// Invoke
compiled_model.Run(input_buffers, output_buffers);

// Read the output
std::vector<float> data(output_data_size);
output_buffers[0].Read<float>(absl::MakeSpan(data));

使用主机内存的零复制

LiteRT CompiledModel API 可减少推理流水线的摩擦,尤其是在处理多个硬件后端和零复制流程时。以下代码段在创建输入缓冲区时使用了 CreateFromHostMemory 方法,该方法使用与主机内存的零复制。

// Define an LiteRT environment to use existing EGL display and context.
const std::vector<Environment::Option> environment_options = {
   {OptionTag::EglDisplay, user_egl_display},
   {OptionTag::EglContext, user_egl_context}};
LITERT_ASSIGN_OR_RETURN(auto env,
   Environment::Create(absl::MakeConstSpan(environment_options)));

// Load model1 and initialize runtime.
LITERT_ASSIGN_OR_RETURN(auto compiled_model1, CompiledModel::Create(env, "model1.tflite", kLiteRtHwAcceleratorGpu));

// Prepare I/O buffers. opengl_buffer is given outside from the producer.
LITERT_ASSIGN_OR_RETURN(auto tensor_type, model.GetInputTensorType("input_name0"));
// Create an input TensorBuffer based on tensor_type that wraps the given OpenGL Buffer.
LITERT_ASSIGN_OR_RETURN(auto tensor_buffer_from_opengl,
    litert::TensorBuffer::CreateFromGlBuffer(env, tensor_type, opengl_buffer));

// Create an input event and attach it to the input buffer. Internally, it creates
// and inserts a fence sync object into the current EGL command queue.
LITERT_ASSIGN_OR_RETURN(auto input_event, Event::CreateManaged(env, LiteRtEventTypeEglSyncFence));
tensor_buffer_from_opengl.SetEvent(std::move(input_event));

std::vector<TensorBuffer> input_buffers;
input_buffers.push_back(std::move(tensor_buffer_from_opengl));

// Create an output TensorBuffer of the model1. It's also used as an input of the model2.
LITERT_ASSIGN_OR_RETURN(auto intermedidate_buffers,  compiled_model1.CreateOutputBuffers());

// Load model2 and initialize runtime.
LITERT_ASSIGN_OR_RETURN(auto compiled_model2, CompiledModel::Create(env, "model2.tflite", kLiteRtHwAcceleratorGpu));
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model2.CreateOutputBuffers());

compiled_model1.RunAsync(input_buffers, intermedidate_buffers);
compiled_model2.RunAsync(intermedidate_buffers, output_buffers);