C++ API 参考
头文件与命名空间
#include <lacinfer/lacinfer.hpp>
除数据类型枚举 lacinfer_shape_datatype_t 外,本章 API 均位于 lacinfer 命名空间。C++ API 将底层句柄封装为对象,并自动管理其创建、复制、移动和销毁;用户通常不需要手动释放相关资源。
数据类型
lacinfer_shape_datatype_t
张量元素类型枚举。
| 枚举值 | 含义 |
|---|---|
lacinfer_shape_tuple_type |
元组形状,不表示普通数值张量 |
lacinfer_shape_fp4x2_type |
两个打包的 4 位浮点值 [实验性] |
lacinfer_shape_bool_type |
布尔值 |
lacinfer_shape_half_type |
IEEE FP16 [实验性] |
lacinfer_shape_float_type |
IEEE FP32 |
lacinfer_shape_double_type |
IEEE FP64 |
lacinfer_shape_uint8_type |
无符号 8 位整数 |
lacinfer_shape_int8_type |
有符号 8 位整数 |
lacinfer_shape_uint16_type |
无符号 16 位整数 |
lacinfer_shape_int16_type |
有符号 16 位整数 |
lacinfer_shape_int32_type |
有符号 32 位整数 |
lacinfer_shape_int64_type |
有符号 64 位整数 |
lacinfer_shape_uint32_type |
无符号 32 位整数 |
lacinfer_shape_uint64_type |
无符号 64 位整数 |
lacinfer_shape_fp8e4m3fnuz_type |
FP8 E4M3 FNUZ1 [实验性] |
lacinfer_shape_fp8e4m3fn_type |
FP8 E4M3 FN [实验性] |
lacinfer_shape_fp8e5m2_type |
FP8 E5M2 [实验性] |
lacinfer_shape_bf16_type |
BF16 [实验性] |
lacinfer_shape_fp8e5m2fnuz_type |
FP8 E5M2 FNUZ [实验性] |
上述 FP4、FP8、FP16 和 BF16 类型在当前版本中属于 [实验性] 类型,其兼容性和行为可能在后续版本中调整。
动态维度 [实验性]
lacinfer::optimals
保存动态维度的优选取值。
explicit optimals(std::initializer_list<size_t> values);
优选值用于提示编译器重点优化的实际维度。每个值应位于动态维度的 [min, max] 范围内。
lacinfer::dynamic_dimension
dynamic_dimension(size_t min, size_t max);
dynamic_dimension(size_t min, size_t max, const optimals& opts);
bool is_fixed() const;
bool operator==(const dynamic_dimension&, const dynamic_dimension&);
bool operator!=(const dynamic_dimension&, const dynamic_dimension&);
| 成员 | 说明 |
|---|---|
min |
允许运行时维度的下界 |
max |
允许运行时维度的上界 |
opts |
可选的优选维度集合 |
is_fixed() |
当最小值与最大值相同时返回 true |
lacinfer::dynamic_dimensions
动态维度容器,用于构造动态 shape。
template<class... Ts>
dynamic_dimensions(const Ts&... dimensions);
size_t size() const;
dynamic_dimension operator[](size_t index) const;
调用 operator[] 前,应确保 index < size();该接口不保证对越界索引进行检查。
张量形状(lacinfer::shape)
描述张量的数据类型、维度和步长。
构造函数
shape();
explicit shape(lacinfer_shape_datatype_t type);
shape(lacinfer_shape_datatype_t type, std::vector<size_t> lengths);
shape(lacinfer_shape_datatype_t type,
std::initializer_list<size_t> lengths);
shape(lacinfer_shape_datatype_t type,
std::vector<size_t> lengths,
std::vector<size_t> strides);
shape(lacinfer_shape_datatype_t type,
const dynamic_dimensions& dyn_dims);
只传
type时构造标量形状。传
lengths时自动计算紧凑布局步长。显式步长构造函数可描述转置、广播或非紧凑布局;
lengths与strides的元素个数必须一致。动态形状使用接受
dynamic_dimensions的构造函数;静态形状使用接受lengths的构造函数。两种构造方式不能混用。默认构造对象不表示可用张量,调用查询成员函数前应先赋入有效形状。
查询成员函数
std::vector<size_t> lengths() const;
std::vector<size_t> strides() const;
dynamic_dimensions dyn_dims() const;
lacinfer_shape_datatype_t type() const;
size_t elements() const;
size_t bytes() const;
bool standard() const;
bool dynamic() const;
size_t index(size_t linear_index) const;
| 成员 | 返回值 |
|---|---|
lengths() |
各维静态长度 |
strides() |
各维步长,以元素为单位 |
dyn_dims() |
各维动态范围;仅对动态形状有意义 |
type() |
元素类型枚举 |
elements() |
逻辑元素总数 |
bytes() |
形状所需字节数 |
standard() |
是否为标准布局 |
dynamic() |
是否包含动态维度 |
index(i) |
将逻辑线性索引映射为缓冲区空间索引 |
形状支持 == 和 !=。比较包含类型及布局信息,而不仅是各维长度。
张量数据(lacinfer::argument)
argument 把 shape 与一段数据缓冲区关联。
构造与访问
argument();
explicit argument(shape s);
argument(shape s, void* buffer);
shape get_shape() const;
char* data() const;
template<typename T>
std::vector<T> as_vector() const;
argument(s)创建与形状对应的空参数对象。argument(s, buffer)引用外部缓冲区,不接管buffer的生命周期。data()返回底层地址。as_vector<T>()按形状索引将数据复制到std::vector<T>;T必须与实际元素类型兼容。
参数序列化
static void save_argument(const argument& value,
const std::string& filename);
static argument load_argument(const std::string& filename);
static argument generate(shape s, size_t seed = 0);
save_argument 与 load_argument 使用参数序列化格式保存或恢复形状和数据。generate 使用指定种子生成测试数据;相同版本、形状和种子可用于构造可重复的测试输入,但不应把它用作安全随机源。
argument 支持 == 和 !=。
编译目标(lacinfer::target)
表示编译目标。
target();
explicit target(const char* name);
当前发布版本支持 "gpu" 和 "ref"。传入其他名称会导致异常。默认构造的对象必须先赋值为有效目标,才能用于编译。
程序输入与输出
lacinfer::program_parameter_shapes
程序输入名到形状的只读映射。
size_t size() const;
shape operator[](const char* name) const;
std::vector<const char*> names() const;
names() 返回的名称指针由底层对象拥有,不应释放;容器失效后不得继续使用这些指针。
lacinfer::program_parameters
程序输入名到参数值的映射。
program_parameters();
program_parameters(
std::initializer_list<std::pair<std::string, argument>> values);
void add(const char* name, const argument& value) const;
名称必须与 program::get_parameter_shapes() 返回的参数名称一致。
lacinfer::arguments
程序输出参数序列。
size_t size() const;
argument operator[](size_t index) const;
该类型支持基于索引的访问以及由公共数组基类提供的迭代、empty() 等容器操作。
lacinfer::shapes
形状序列。
size_t size() const;
shape operator[](size_t index) const;
建图对象
lacinfer::operation
描述操作名称与属性。
template<class... Ts>
operation(const char* name,
const char* json_attributes = nullptr,
Ts... values);
std::string name();
json_attributes 是操作属性的 JSON 文本。属性集合由相应操作定义决定。
lacinfer::instruction
模块中指令的不透明引用。它主要作为 module 添加指令和返回值时的输入或结果使用。
lacinfer::instructions 与 lacinfer::modules
template<class... Ts>
instructions(const Ts&... instructions);
template<class... Ts>
modules(const Ts&... modules);
这两个辅助容器分别向 API 传递指令序列与子模块序列。
lacinfer::module
module 表示程序中的一个计算图模块。它引用所属 program 的内部数据;使用 module 对象期间,必须确保对应的 program 仍然有效。
void print() const;
instruction add_instruction(const operation& op,
const instructions& args);
instruction add_instruction(const operation& op,
const instructions& args,
const modules& module_args);
template<typename T>
instruction add_literal(const shape& s, T* buffer);
instruction add_parameter(const std::string& name, shape s);
instruction add_return(const instructions& args);
instruction add_allocation(const shape& s);
add_literal 会把给定数据加入图中;缓冲区必须至少包含 s.bytes() 字节。带 module_args 的 add_instruction 用于控制流等具有子模块参数的操作。
编译选项
lacinfer::compile_options
compile_options();
void set_offload_copy(bool value = true);
void set_fast_math(bool value = true);
void set_exhaustive_tune_flag(bool value = true);
| 选项 | 说明 |
|---|---|
offload_copy |
为 GPU 等使用独立设备内存的目标,在编译时加入必要的数据复制:将输入从主机内存复制到设备内存,并将结果复制回主机内存 |
fast_math |
使用更快的近似数学实现,可能产生轻微精度差异 |
exhaustive_tune |
扩大内核搜索范围以寻找更快实现,增加首次编译时间 |
程序(lacinfer::program)
程序保存一个或多个模块,并提供编译和执行入口。
构造、编译与查询
program();
void compile(const target& t) const;
void compile(const target& t, const compile_options& options) const;
program_parameter_shapes get_parameter_shapes() const;
shapes get_output_shapes() const;
compile 原地修改程序。量化和图修改应在编译前完成;已为一个目标编译的程序不应直接当作另一个目标的未编译程序使用。
执行
arguments eval(const program_parameters& params) const;
template<class Stream>
arguments run_async(const program_parameters& params, Stream* stream) const;
eval执行已编译程序并返回全部输出。run_async在调用方提供的原生流上提交工作。流类型名会传递给运行时,因此必须使用执行目标支持的流指针类型。输入名称、类型、形状和内存位置必须符合编译后的程序要求。
图与诊断
void print() const;
program sort();
module get_main_module();
module create_module(const std::string& name);
sort() 原地按拓扑关系整理程序并返回当前程序。print() 向标准输出打印程序文本。create_module() 创建命名子模块。
程序支持 == 和 !=。
ONNX 解析
lacinfer::onnx_options
onnx_options();
void set_input_parameter_shape(const std::string& name,
std::vector<size_t> dims);
void set_dyn_input_parameter_shape(const std::string& name,
const dynamic_dimensions& dims);
void set_default_dim_value(unsigned int value);
void set_default_dyn_dim_value(const dynamic_dimension& dim);
void set_default_loop_iterations(int64_t value);
void set_limit_loop_iterations(int64_t value);
void set_external_data_path(const std::string& path);
| 成员 | 说明 |
|---|---|
set_input_parameter_shape |
覆盖指定输入的静态维度 |
set_dyn_input_parameter_shape |
覆盖指定输入的动态维度范围 |
set_default_dim_value |
为未确定的维度使用固定默认值 |
set_default_dyn_dim_value |
为未确定的维度使用默认动态范围 |
set_default_loop_iterations |
当 Loop2 未提供迭代次数时采用的默认值 |
set_limit_loop_iterations |
Loop 允许的最大迭代上限 |
set_external_data_path |
ONNX 外部权重文件的绝对目录 |
同一个输入不要同时配置静态和动态覆盖。外部数据路径应指向文件所在目录,而不是单个权重文件。
文件解析
program parse_onnx(const char* filename);
program parse_onnx(const char* filename, const onnx_options& options);
内存解析
program parse_onnx_buffer(const void* data, size_t size);
program parse_onnx_buffer(const void* data,
size_t size,
const onnx_options& options);
program parse_onnx_buffer(const std::string& buffer);
program parse_onnx_buffer(const std::string& buffer,
const onnx_options& options);
缓冲区必须包含完整模型数据,并在函数返回前保持有效。解析成功后返回未编译的 program。
查询 ONNX 操作
std::vector<std::string> get_onnx_operators();
返回当前构建支持的 ONNX 操作名称。
量化
量化 API 原地修改程序,应在 program::compile() 之前调用。
INT8
struct quantize_int8_options
{
quantize_int8_options();
void add_op_name(const std::string& name);
void add_calibration_data(const program_parameters& params);
void set_force_i8_output(bool value);
void set_save_quantized_lir_path(const std::string& path);
void add_asymmetric_activation_op(const std::string& op_name);
void add_per_channel_op(const std::string& op_name);
void add_node_to_exclude(const std::string& node_name);
};
void quantize_int8(const program& p,
const target& t,
const quantize_int8_options& options);
| 配置 | 说明 |
|---|---|
add_op_name |
增加需要量化的操作名 |
add_calibration_data |
增加一组校准数据 |
set_force_i8_output |
为可量化的浮点模型输出插入对称 INT8 量化 [实验性] |
set_save_quantized_lir_path |
非空时将量化后的程序以 LIR 格式保存到指定路径 |
add_asymmetric_activation_op |
对指定操作名的激活输入启用非对称量化 [实验性] |
add_per_channel_op |
对指定操作启用逐通道量化 |
add_node_to_exclude |
按源模型节点名排除量化;名称精确匹配 [实验性] |
INT8 校准数据的参数名、类型与形状必须覆盖程序输入。目标用于执行校准过程。
add_op_name 接受 LACInfer 内部操作名,而不是源模型节点名。未调用该方法时,默认量化以下所有支持操作;调用后,只量化通过该方法加入的操作。
add_op_name 参数值 |
操作 |
|---|---|
dot |
点积或矩阵乘法 |
convolution |
正向卷积 |
convolution_backwards |
反向卷积 |
pooling |
仅支持平均池化(Average Pooling) [实验性] |
add |
加法 |
sdpa |
缩放点积注意力 |
逐通道量化当前支持以下操作名:
add_per_channel_op 参数值 |
量化对象 | 通道轴 |
|---|---|---|
convolution |
第 2 个输入,即权重 | 0 |
convolution_backwards |
第 2 个输入,即权重 | 1 |
add_asymmetric_activation_op 当前支持以下操作名:
| 参数值 | 量化对象 |
|---|---|
convolution |
第 1 个输入,即激活输入;权重仍采用对称量化 |
启用非对称激活量化会导致性能下降,当前版本不推荐使用;仅在对 mAP 有极高要求,并经实际模型精度与性能测试确认收益后考虑启用。
nodes_to_exclude 只对具有源模型节点名、且进入 INT8 量化候选的节点生效。名称必须精确匹配;未命名、名称未知或在量化前已被优化掉的节点不会被排除。
FP16 与 BF16 [实验性]
struct quantize_op_names
{
quantize_op_names();
void add(const std::string& name);
};
void quantize_fp16(const program& p);
void quantize_fp16(const program& p, const quantize_op_names& names);
void quantize_bf16(const program& p);
void quantize_bf16(const program& p, const quantize_op_names& names);
不传 names 时使用默认操作集合;传入集合时只转换指定操作类型。
FP8 [实验性]
struct quantize_fp8_options
{
quantize_fp8_options();
void add_calibration_data(const program_parameters& params);
};
void quantize_fp8(const program& p,
const target& t,
const quantize_fp8_options& options);
quantize_fp8() 使用校准数据将程序原地量化为 FP8。每组校准数据的参数名、类型和形状必须与程序输入匹配;目标 t 用于执行校准过程。
程序持久化
lacinfer::file_options
file_options();
void set_file_format(const char* format);
支持的格式为 "msgpack" 和 "json"。MsgPack 更紧凑,适合部署;JSON 便于检查和调试。
load
program load(const char* filename);
program load(const char* filename, const file_options& options);
加载已序列化的程序。文件是否包含可直接运行的已编译状态取决于保存内容和运行环境;部署时应保证目标和软件版本兼容。
save
void save(const program& p, const char* filename);
void save(const program& p,
const char* filename,
const file_options& options);
上下文与异步执行
context program::experimental_get_context();
返回由程序编译目标创建的执行上下文。该上下文保存目标相关的运行时状态;例如,在 GPU 目标下可用于等待任务完成或获取原生执行队列。此接口为 [实验性]。
void context::finish() const;
等待上下文中的工作完成。
template<class T>
T context::get_queue();
将原生队列地址转换为调用方指定类型,类型错误会导致未定义行为;仅在需要与外部运行时共享队列时使用。
1. E4 表示指数部分占 4 位(Exponent),M3 表示尾数部分占 3 位(Mantissa);FN 表示支持有限数和 NaN,但不支持无穷大(Finite and NaN);UZ 表示零不区分正负(Unsigned Zero)。 ↩
2. Loop 是 ONNX 的循环控制流操作,它根据最大迭代次数和循环条件重复执行循环体子图。 ↩