news 2026/9/10 22:56:38

CANN/GE DT用例开发总纲

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN/GE DT用例开发总纲

GE DT用例开发总纲

【免费下载链接】geGE(Graph Engine)是面向昇腾的图编译器和执行器,提供了计算图优化、多流并行、内存复用和模型下沉等技术手段,加速模型执行效率,减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力,并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge

本文针对UT和ST两类测试,结合DT测试中常见的问题,确定用例规范、基本要求。本文默认读者已经有了基本的UT/ST开发经验,可以实现UT/ST用例,因此关于用例的基础写法不会做详细介绍。

本文仅指导如何写UT/ST,请使用ge-dt-runnerskill编译运行,或参考docs/build.md全量执行用例

详细指导

文档内容适用场景
测试框架指南测试基类、Graph DSL、faker、stub、checker构造输入、打桩、校验输出时查阅
UT用例开发指导UT设计checklist、校验方法、图类UT、rt2 kernel UT编写UT用例时查阅
ST用例开发指导ST注释要求、场景checklist、图编译/执行校验、DUMP阶段列表编写ST用例时查阅

基础知识

何时新增DT测试用例

一般来说,在两种情况下新增DT测试用例:

  1. 新需求开发时,基于模块设计,设计并新增UT/ST用例

  2. 修复问题时,包含自验证发现的问题、上库时流水线发现的问题、上库后被提单的问题等等。理论上只要发现了一个bug,并且你在修复它,就需要新增DT测试用例,根据被测的行为,你可以选择新增UT用例或ST用例,或者两者都各增加几个。建议的问题修复流程为:

DT测试工程

GE仓UT/ST代码位于tests目录下,tests目录下的子目录作用:

tests - acl_ut: ACL接口的UT用例目录 - autofuse: 自动融合相关的UT/ST用例目录 - benchmark: 性能用例的目录 - bin: 测试可执行产物及kernel二进制目录 - cmake: 测试工程的公共cmake脚本 - depends: 底层依赖的桩代码和测试辅助工具,各子目录说明如下: - slog: 日志接口桩(slog、dlog、ascendalog) - runtime: 昇腾运行时桩(rtMalloc、rtMemcpy、rtLaunch、stream/event等) - hccl: 集合通信桩(HCCL AllReduce等) - ascendcl: AscendCL API桩(acl_rt、设备管理、内存等) - op_stub: 算子实现桩,提供fake算子实现供测试图执行器调度 - profiler: Profiling/Dump桩 - platform: 平台/芯片信息查询桩 - error_manager: 错误管理桩 - graph_tuner: 图调优桩 - aoe: AOE(Ascend Optimization Engine)桩 - mmpa/mmpa2: 平台无关的内存/进程抽象API桩 - llm_datadist: LLM数据分发测试辅助桩 - helper_runtime: 辅助运行时桩(tsd_client、grpc_server等) - aihacb_autofusion: 自动融合桩 - python: Python绑定的LLM封装模块 - checker: 仅头文件的测试校验工具(mem_trace_checker、shape_checker等) - conf: 测试配置数据(如error_code.json) - symbol: 仅头文件的符号形状推断测试辅助工具 - dflow: dflow模块的UT/ST用例目录(含flow_graph、llm_datadist、pydflow、runner、udf等子模块) - docs: 测试相关文档目录 - engines: 各执行引擎的UT/ST用例目录(含cpueng、dvppeng、ffts_engine、hccl_engine、nn_engine、rts_engine、te_fusion) - framework: 框架目录,包含UT/ST的公用测试代码,例如faker、stub、easy_graph等 - ge: GE核心模块的UT/ST用例目录(含ut、st子目录) - graph_metadef: 图元数据定义模块的UT/benchmark用例目录 - parser: 模型解析模块的UT/ST用例目录 - python_tests: Python接口测试目录 - test_c: C接口测试目录

不论是UT还是ST,我们均使用googletest测试框架,并提供了AddressSanitizer来做内存类检查、gcov来做覆盖率统计。对于新增代码,UT覆盖率要求超过90%,ST覆盖率要求超过80%。覆盖率统计的具体操作请参考docs/build.md,基本命令为:

# 带 -c 参数运行测试,自动生成覆盖率统计文件到 cov/ 目录 bash tests/run_test.sh -c [其他参数]

前置条件:需安装lcovsudo apt-get install lcov)和pip3 install coverage,且编译和运行环境的 gcc/gcov 版本需一致。

新用例文件加入到构建系统

GE测试工程使用标准CMake构建,没有自定义的注册宏。新增用例文件后,需修改对应目录的CMakeLists.txt。将源文件加入构建变量

# 方式一:添加到显式列出的源文件列表中 set(MY_TEST_FILES "graph/optimize/foo_unittest.cc" ) # 方式二:已有 file(GLOB_RECURSE ...) 时,新文件会被自动收录

用例规范

规范1:UT用例文件名为"所测文件名_unittest.cc"

例如有一个类为Foo,其位于foo.cc中,那么对应的UT用例文件名为:foo_unittest.cc

如果使用googletest的测试套+测试用例的方式,那么测试套的类名为FooUT,代码说明:

// foo_unittest.cc #include <gtest/gtest.h> class FooUT : public testing::Test {}; // 测试套定义,类名为 FooUT TEST_F(FooUT, case_name) { // 测试用例定义 // ... }

使用规范化的命名,可以让后续开发者查阅用例时更容易找到对应文件,所以务必遵守。

规范2:ST用例文件名为"所测特性_systemtest.cc"

例如零拷贝的ST用例可以被命名为:zero_copy_systemtest.cczero_copy_system_test.cc也是被接受的。ST用例的测试粒度为特性,不要为了覆盖率而用覆盖率来测试某个模块,例如tensor_systemtest.cc

规范3:UT用例名

UT用例名使用如下模板:被测接口_预期行为_场景,被下划线分割的每一部分,均使用大驼峰命名。例如对FrameSelector的测试中,希望测试接口SelectMainRoot,当前图为Main图的子图时,预期行为是,调用此接口后,创建的Node仍然位于Main图上,用例名为:SelectMainRoot_CreateOnMainRoot_CurrentFrameIsMainSubgraphs

有时仅期望接口成功,那么用例名也可以简写为被测接口_场景Ok,例如测试Tensor的默认构造可以成功构建出Tensor,用例名可以为DefaultConstructor_ConstructOk

有时一个模块分为很多子功能,此时用例名的第一个字段也可以用子功能的名字:子功能_预期行为_场景

规范4:一个用例只校验一个场景

不论是UT还是ST用例,一个用例仅应该构造一个场景,并将其校验完整。如果有其他场景,需要新写一个用例测试。这样做的缺点是可能存在部分的代码冗余和牺牲一些运行效率,换来的优点是后期易于维护。

举例说明,如下用例是一个正面例子,该用例通过构造函数构造了Tensor,并完整校验了默认值是否符合预期:

TEST_F(TensorUT, ConstructOk_V2) { TensorV2 tensor{{{8, 3, 224, 224}, {16, 3, 224, 224}}, // shape {ge::FORMAT_ND, ge::FORMAT_FRACTAL_NZ, {}}, // format kOnDeviceHbm, // placement ge::DT_FLOAT16, // dt nullptr}; const TensorV2 &t2 = tensor; EXPECT_EQ(t2.GetOriginShape(), Shape({8, 3, 224, 224})); EXPECT_EQ(t2.GetStorageShape(), Shape({16, 3, 224, 224})); EXPECT_EQ(t2.GetOriginFormat(), ge::FORMAT_ND); EXPECT_EQ(t2.GetStorageFormat(), ge::FORMAT_FRACTAL_NZ); StorageFormat storage_format{ge::FORMAT_ND, ge::FORMAT_FRACTAL_NZ, {}}; EXPECT_EQ(t2.GetFormat(), storage_format); EXPECT_EQ(t2.GetExpandDimsType(), ExpandDimsType{}); EXPECT_EQ(t2.GetPlacement(), kOnDeviceHbm); EXPECT_EQ(t2.GetDataType(), ge::DT_FLOAT16); EXPECT_EQ(t2.GetAddr(), nullptr); EXPECT_EQ(t2.GetData<int64_t>(), nullptr); }

可以接受的例子:虽然在一个用例中校验了多个子场景,但还好都是校验空指针场景;

TEST_F(ModelOutpusUT, UpdateOutputShape_Failed_WhenNullptr) { StorageShape shape = {{8, 3, 224, 224}, {8, 1, 224, 224, 16}}; kernel::BuildTensorAttr attr = {kOnHost, ge::DT_FLOAT16, {ge::FORMAT_NCHW, ge::FORMAT_NC1HWC0, {}}}; auto tensor = TensorFaker().Shape({}).Format(ge::FORMAT_ND).DataType(ge::DT_FLOAT).Build(); // 首参数为空 auto context = KernelRunContextFaker().Inputs({nullptr, tensor.GetTensor(), &attr}).Build(); ASSERT_NE(kernel::UpdateOutputShape(context.GetContext()), ge::GRAPH_SUCCESS); // 第二个参数为空 context = KernelRunContextFaker().Inputs({&shape, nullptr, &attr}).Build(); ASSERT_NE(kernel::UpdateOutputShape(context.GetContext()), ge::GRAPH_SUCCESS); // 第三个参数为空 context = KernelRunContextFaker().Inputs({&shape, tensor.GetTensor(), nullptr}).Build(); ASSERT_NE(kernel::UpdateOutputShape(context.GetContext()), ge::GRAPH_SUCCESS); }

如下例子不可接受:

TEST_F(SinkNodeBinTest, test_sink_node_bin_with_handle_success) { kernel::BinData bin_data; const char *bin_key1 = "key1"; const char *bin_key2 = "key2"; const char *empty_key = ""; struct FakeRuntime : RuntimeStubImpl { rtError_t rtRegisterAllKernel(const rtDevBinary_t *bin, void **handle) { static size_t registed_num = 0x10; *handle = (void *)(registed_num++); return RT_ERROR_NONE; } }; GertRuntimeStub runtime(std::unique_ptr<RuntimeStubImpl>(new FakeRuntime())); auto run_context = BuildKernelRunContext(2, 1); run_context.value_holder[0].Set((void *)&bin_data, nullptr); run_context.value_holder[1].Set((void *)bin_key1, nullptr); ASSERT_EQ(kernel::SinkNodeBinWithHandle(run_context.GetContext<KernelContext>()), ge::GRAPH_SUCCESS); ASSERT_EQ(run_context.value_holder[2].GetValue<uint64_t>(), 0x10); run_context = BuildKernelRunContext(2, 1); run_context.value_holder[0].Set((void *)&bin_data, nullptr); run_context.value_holder[1].Set((void *)bin_key2, nullptr); ASSERT_EQ(kernel::SinkNodeBinWithHandle(run_context.GetContext<KernelContext>()), ge::GRAPH_SUCCESS); ASSERT_EQ(run_context.value_holder[2].GetValue<uint64_t>(), 0x10 + 1); run_context = BuildKernelRunContext(2, 1); run_context.value_holder[0].Set((void *)&bin_data, nullptr); run_context.value_holder[1].Set((void *)bin_key1, nullptr); ASSERT_EQ(kernel::SinkNodeBinWithHandle(run_context.GetContext<KernelContext>()), ge::GRAPH_SUCCESS); ASSERT_EQ(run_context.value_holder[2].GetValue<uint64_t>(), 0x10); // cached run_context = BuildKernelRunContext(2, 1); run_context.value_holder[0].Set((void *)&bin_data, nullptr); run_context.value_holder[1].Set((void *)empty_key, nullptr); ASSERT_EQ(kernel::SinkNodeBinWithHandle(run_context.GetContext<KernelContext>()), ge::GRAPH_SUCCESS); ASSERT_EQ(run_context.value_holder[2].GetValue<uint64_t>(), 0x10 + 2); run_context = BuildKernelRunContext(2, 1); run_context.value_holder[0].Set((void *)&bin_data, nullptr); run_context.value_holder[1].Set((void *)empty_key, nullptr); ASSERT_EQ(kernel::SinkNodeBinWithHandle(run_context.GetContext<KernelContext>()), ge::GRAPH_SUCCESS); ASSERT_EQ(run_context.value_holder[2].GetValue<uint64_t>(), 0x10 + 3); }

上述例子中,测试了SinkBin可以成功、测试了多次Sink同一个bin会做cache,测试了empty key的场景,这导致此测试用例的测试点很分散、测试意图不清晰、用例名也无法取得简单,导致了后续的维护困难。

规范5:不允许将private define为public

不论是UT还是ST,均是基于公开接口测试,即使是UT,也是基于模块的公开接口做测试。private函数、成员都是模块的内部实现,不应该成为测试或校验点的范围。这是为了用例的稳定性考虑,因为我们认为,一个模块的行为是相对稳定的,但是模块的内部实现是可以随意变化的。

规范6:用例中不允许打开Debug级别日志或Dump图开关

Debug级别日志、Dump图开关一般来说是用于开发者调试的工具,打开Debug级别日志或Dump图开关会极大地影响代码的执行速度,在一个用例中打开了上述开关,影响到此用例及其后继执行的所有用例,进而导致整个UT/ST工程执行速度很慢。因此请不要打开此开关,如果为了调试方便,那么仅在调试时打开,上库时需要将此开关关闭

例外:

  • 针对Debug日志、Dump图测试时,可以打开开关,但是需要在用例结束时关闭

规范7:用例不可以有副作用

一个用例的执行不应该对其他用例产生影响,也不应该对自己产生影响。例如:

  • 如果用例中产生或修改了磁盘文件,那么在TearDown中,需要将对应文件删除或恢复
  • 如果用例中新增或修改了环境变量,那么在TearDown中,需要将其恢复
  • 如果用例中改变了单例或全局变量的状态,那么在TearDown中,需要恢复

规范8:不可以依赖外部的初始化动作

用例的初始化/去初始化操作应该位于本用例的setup/teardown函数中进行,一个用例不应该依赖于外部的初始化。例如部分用例需要在执行该用例前,将某些so拷贝到指定路径才可以正确执行。如果一个用例依赖于某些so在特定路径下,该用例应该在自己的setup函数中完成so的拷贝、在teardown中完成so的删除。

【免费下载链接】geGE(Graph Engine)是面向昇腾的图编译器和执行器,提供了计算图优化、多流并行、内存复用和模型下沉等技术手段,加速模型执行效率,减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力,并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 22:55:27

机器学习入门:手写线性回归与房价预测实战

我入行时做的第一个真正意义上的机器学习模型&#xff0c;不是神经网络&#xff0c;而是线性回归。后来带新人&#xff0c;我也总是先让他们把线性回归从头撸一遍。不是因为简单&#xff0c;而是因为它把机器学习的核心逻辑全串在了一起&#xff1a;数据怎么处理、模型怎么定义…

作者头像 李华
网站建设 2026/9/10 22:54:33

CANN/ge图引擎节点构建API

Build 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友…

作者头像 李华
网站建设 2026/9/10 22:51:05

Scikit-learn入门:从数据预处理到模型部署全流程

1. 为什么选择Scikit-learn作为机器学习入门工具在数据科学和机器学习领域&#xff0c;Scikit-learn&#xff08;简称sklearn&#xff09;已经成为Python生态中最受欢迎的机器学习库之一。作为一个从业多年的数据科学家&#xff0c;我依然清晰记得第一次使用这个工具时的惊喜—…

作者头像 李华
网站建设 2026/9/10 22:50:49

昇腾GE模型输入数量接口

aclmdlGetNumInputs 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、Tensor…

作者头像 李华