gen_case
是每次调用任一算子接口 api 时,根据 api 中形参数,生成一份该算子的 prototxt
测例,实现该功能的两点收益:
1)在框架跑一遍网络后,便可生成该网络调用到的各算子的所有规模测例,方便收集真实网络的规模数据,作为性能测试的数据集。
2)当框架调用网络时因为算子错误报错,或算子单元测试失败时,我们可以开启自动生成测例功能,获取会导致算子执行失败的测例规模与参数、更方便定位问题。
相比 generator
,gen_case
是在运行算子时保存测试用例,generator
是运行算子前创建测试用例供算子使用。在 MLUOPS 工程中,GEN_CASE
主要以gen_case.h
和 gen_case.cpp
两个文件添加到工程中,文件中涉及到的数据结构或是类型,主要存在于 mlu_op_core.h
、core
文件夹和 mlu_op_test.proto
中。
- 在测试机上打开环境变量
export MLUOP_GEN_CASE = 1
- 执行
cd build/test/ ./mluop_gtest --gtest_filter = *abs*
- 结果 会在当前目录下生成 gen_case/abs 文件夹,在文件夹里的 *.prototxt 文件保存了算子测试过程中测例规模
环境变量 | 功能 | 默认状态 |
---|---|---|
MLUOP_GEN_CASE | export MLUOP_GEN_CASE = 0: 关闭gen_case模块功能; export MLUOP_GEN_CASE = 1: 生成 prototxt, 输入输出只保留 shape 等信息(GEN_CASE_DATA_REAL将无效); export MLUOP_GEN_CASE = 2: 生成 proto, 并保留输入真实值; export MLUOP_GEN_CASE = 3: 不生成 prototxt, 只在屏幕上打印输入输出的shape等信息。 |
默认 0 |
MLUOP_GEN_CASE_OP_NAME | export MLUOP_GEN_CASE_OP_NAME = "算子A; 算子B……": 指定只使能算子 A/B……的 gen_case 功能; export MLUOP_GEN_CASE_OP_NAME = "-算子A; -算子B……": 指定只不使能算子 A/B……的 gen_case 功能。 |
默认全部算子使能 |
MLUOP_GEN_CASE_DUMP_DATA | 在MLUOP_GEN_CASE = 2时生效; export MLUOP_GEN_CASE_DUMP_DATA = 0: prototxt 中不保存输入的真值(此时的GEN_CASE_DATA_REAL有效); export MLUOP_GEN_CASE_DUMP_DATA = 1: prototxt 中保存输入的文本形式真值; export MLUOP_GEN_CASE_DUMP_DATA = 2: prototxt 中保存输入的二进制真值。 |
默认 0 |
MLUOP_GEN_CASE_DUMP_DATA_OUTPUT | export MLUOP_GEN_CASE_DUMP_DATA_OUTPUT = 0: prototxt 中不保存 mlu 的输出值; export MLUOP_GEN_CASE_DUMP_DATA_OUTPUT = 1: prototxt 中保存文本形式的 mlu 输出值; export MLUOP_GEN_CASE_DUMP_DATA_OUTPUT = 2: prototxt 中保存二进制形式的 mlu 输出值。 |
默认 0 |
MLUOP_GEN_CASE_DUMP_DATA_FILE | 在 MLUOP_GEN_CASE = 2时生效; export MLUOP_GEN_CASE_DUMP_DATA_FILE = 0: 保存方式以 MLUOP_GEN_CASE_DUMP_DATA 为准 export MLUOP_GEN_CASE_DUMP_DATA_FILE = 1: 真实值以一个二进制文件单独存储, prototxt 文件中保存 path。 |
默认 0 |
以上内容主要是面向使用者,如何使用 gen_case 来获取算子测试用例,这里将面向算子开发着如何去写代码调用 gen_case 的模块生成算子测例。
gen_case宏函数说明,gen_case.h 主要定义了多个宏函数,在使用的时候主要利用这些宏来生成测试用例。
宏函数名 | 函数功能说明 | 接口数据 | 数据类型 |
---|---|---|---|
GEN_CASE_START (op_name) |
根据输入的 op_name,在当前目录下创建 gen_case/op_name 文件夹,并在文件夹里创建名为 "op_name_时间.prototxt文件",同时将 op_name 写入文件开头 | op_name | std::string |
GEN_CASE_DATA (is_input, id,data, data_desc, upper_bound, lower_bound) |
根据给入的参数对 prototxt 文件写入 input{} 结构或者 output{} 结构。 | is_input id data data_desc upper_bound lower_bound |
bool std::string void* mluOPTensorDescriptor_t double double |
GEN_CASE_DATA_v2 (is_input, id,data, data_desc, upper_bound, lower_bound, distribution) |
根据给入的参数对 prototxt 文件写入 input{}结构或者 output{}结构。 | is_input id data data_desc upper_bound lower_bound distribution |
bool std::string void* mluOPTensorDescriptor_t double double std::string |
GEN_CASE_DATA_UNFOLD (is_input,id,data,dim,dims,dtype,layout, upper_bound, lower_bound) |
根据给入的参数对 prototxt 文件写入 input{}结构或者 output{}结构,将 data_desc 展开成 dim, dims, dtype 和 layout |
is_input id data dim dims dtype layout upper_bound lower_bound |
bool std::string void* const int std::vector/int* mluOPDataType_t mluOPTensorLayout_t double double |
GEN_CASE_DATA_UNFOLD_v2 (is_input,id, data, dim,dims, dtype, layout, upper_bound, lower_bound, distribution) |
相比 GEN_CASE_DATA_UNFOLD 增加了 distribution 参数,用来表示生成数据服从的分布 | is_input id data dim dims dtype layout upper_bound lower_bound distribution |
bool std::string void* const int std::vector/int* mluOPDataType_t mluOPTensorLayout_t double double std::string |
GEN_CASE_OP_PARAM_SINGLE (flag,param_node_name, param_name, value) |
根据传入的参数生成 op_name_param:{} 数据结构,其中该函数要求参数必须是单个数据。 | flag param_node_name param_name value |
int std::string std::string 基础数据类型 |
GEN_CASE_TEST_PARAM (is_diff1,is_diff2, is_diff3, diff1_threshould, diff2_threshold, diff3_threshold…) |
根据传入的参数决定测例的 diff 模式和精度误差阈值 | is_diff1 is_diff2 is_diff3 diff1_threshold diff2_threshold diff3_threshold diff1_threshold_imag diff2_threshold_imag diff3_threshold_imag |
bool bool bool const float const float const float const float const float const float |
GEN_CASE_DATA_REAL (is_input,id,data, data_desc) |
根据给入的参数对 prototxt 文件写入 input{} 结构,并保存 input tensor 里的真实数据 | is_input id data data_desc |
bool std::string void* mluOpTensorDescriptor_t |
GEN_CASE_HANDLE (handle) |
设置 handle | handle | mluOPHandle_t |
GEN_CASE_HANDLE_PARAM () |
打印设置的 handle 中的信息 | ||
GEN_CASE_OP_PARAM_SINGLE_NAME (flag,param_node_name, param_name, value) |
如果 op_name 和参数名字不一致时,可以用这个函数设置 | flag param_node_name param_name value |
int std::string std::string 基础数据类型 |
GEN_CASE_OP_PARAM_SINGLE_SUB (flag,param_node_name, param_name, value, new_child) |
如果参数又嵌套子参数,可以用这个函数这设置 | flag param_node_name param_name value new_child |
int std::string std::string 基础数据类型 bool |
GEN_CASE_OP_PARAM_ARRAY (flag,param_node_name, param_name, value, num) |
根据传入的参数生成 op_name_param:{}结构。其中该函数要求参数必须是数组类型 | flag param_node_name param_name value[] num |
int std::string std::string 基础数据类型 const int |
GEN_CASE_END () |
恢复gen_case相关状态 |
以下内容以 mlu-ops 仓库中 roi_crop_forward 算子为例在 host 端进行 gen_case 功能代码开发
1)添加头文件
在 bangc-docs/kernel/算子名文件夹/op_name.cpp 文件下添加头文件
#include "core/context.h"
#include "core/gen_case.h"
2)功能代码编写
mluOpStatus_t MLUOP_WIN_API mluOpRoiCropForward(
mluOpHandle_t handle, const mluOpTensorDescriptor_t input_desc,
const void *input, const mluOpTensorDescriptor_t grid_desc,
const void *grid, const mluOpTensorDescriptor_t output_desc, void *output) {
……
if (MLUOP_GEN_CASE_ON_NEW)
{
GEN_CASE_START("roi_crop_forward");
GEN_CASE_HANDLE(handle);
GEN_CASE_DATA(true, "input", input, input_desc, -10, 10);
GEN_CASE_DATA(true, "grid", grid, grid_desc, -1, 1);
GEN_CASE_DATA(false, "output", output, output_desc, 0, 0);
GEN_CASE_TEST_PARAM_NEW(true, true, false, 0.003, 0.003, 0);
}
cnrtDim3_t k_dim;
cnrtFunctionType_t k_type;
policyFunc(handle, bin_num, &k_dim, &k_type);
VLOG(5) << "[mluOpRoiCropForward] launch kernel policyFunc[" << k_dim.x
<< ", " << k_dim.y << ", " << k_dim.z << "].";
KERNEL_CHECK((mluOpBlockKernelRoiCropForwardFloat(
k_dim, k_type, handle->queue, input, grid, batch, height, width, channels,
grid_n, output_h, output_w, output)));
VLOG(5) << "Kernel mluOpBlockKernelRoiCropForwardFloat.";
GEN_CASE_END();
return MLUOP_STATUS_SUCCESS;
}