- 人工智能
- 深度学习
- 算子库
- CANN
- Ascend
【免费下载链接】asc-devkit
本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。
在 asc-devkit 的 Tiling 调测(Tiling tuning)工具中,context_ascendc::ContextBuilder是手动构造TilingContext与KernelContext的核心入口类,用于脱离图引擎环境验证 Tiling 函数与 TilingParse 函数。本文以 ContextBuilder 构造函数 官方文档为主体,结合仓库头文件、实现源码与单元测试,剖析该无参构造函数“初始化数据成员”这一行为的具体实现:它如何通过 pimpl(pointer to implementation)模式持有内部实现,并为后续 Builder 链式调用建立哪些默认状态,以及非拷贝、禁移动的设计约束对使用者意味着什么。
ContextBuilder 在 Tiling 调测中的定位
Tiling 调测工具属于 asc-devkit 的 Utils-API 能力之一,其文档索引见 Tiling_tuning,包含OpTilingRegistry与ContextBuilder两组接口。其中 ContextBuilder 简介文档说明:
ContextBuilder 类提供一系列的 API 接口,支持手动构造 TilingContext 类来验证 Tiling 函数以及 KernelContext 类用于 TilingParse 函数的验证。
也就是说,开发者在独立调测一个算子的 Tiling 函数(如MyAddTiling::Init)或TilingParse时,可以不必依赖完整的模型推理环境,而是通过ContextBuilder手工拼出与运行时等价的数据结构。ContextBuilder的全部成员声明位于 context_builder.h:
class ContextBuilder { public: ContextBuilder(); ~ContextBuilder(); ContextBuilder(ContextBuilder &&kernelRunContextBuilder) = delete; ContextBuilder &operator=(ContextBuilder &&kernelRunContextBuilder) = delete; // kernel context builder ContextBuilder &Inputs(std::vector<void *> inputs); ContextBuilder &Outputs(std::vector<void *> outputs); std::shared_ptr<KernelRunContextHolder> BuildKernelRunContext(); // OpInfo ContextBuilder &NodeIoNum(size_t inputNum, size_t outputNum); ContextBuilder &SetOpNameType(const std::string& opName, const std::string& opType); // ... AddInputTd / AddOutputTd / AddAttr / CompileInfo / PlatformInfo / // AddPlatformInfo / TilingData / Workspace 等设置接口,均返回 ContextBuilder& std::shared_ptr<KernelRunContextHolder> BuildTilingContext(); private: std::unique_ptr<ContextBuilderImpl> impl_; };从源码结构看,该类是典型的Builder 模式 + pimpl 惯用法:对外暴露链式设置接口(每个 setter 返回ContextBuilder&),内部仅持有一个std::unique_ptr<ContextBuilderImpl> impl_,将所有状态转移到私有实现类 ContextBuilderImpl。构造函数正是建立这一内部状态的第一步。
函数原型、参数与约束
官方文档对ContextBuilder构造函数的定义如下(完整继承自 ContextBuilder_constructor.md):
- 功能说明:创建 ContextBuilder 对象时,初始化数据成员。
- 函数原型:
ContextBuilder()- 参数说明:无。
- 约束说明:无。
这是一个无参默认构造函数,且类声明未提供任何其他构造方式(没有带参构造,也没有工厂函数),因此每一个可用的ContextBuilder对象都必然经由该构造函数创建。下面结合实现看“初始化数据成员”究竟做了什么。
构造函数做了什么:从 ContextBuilder 到 ContextBuilderImpl
ContextBuilder构造函数的实现非常简短,位于 context_builder.cpp:
ContextBuilder::ContextBuilder() { impl_.reset(new ContextBuilderImpl); } ContextBuilder::~ContextBuilder() = default;它做且仅做一件事:为impl_分配一个ContextBuilderImpl实例。真正的“数据成员初始化”发生在ContextBuilderImpl的构造函数中,见 context_builder_impl.cpp:
ContextBuilderImpl::ContextBuilderImpl() { kernelCtxBuilder_ = std::make_unique<gert::OpKernelContextBuilder>(); tilingCtxBuilder_ = std::make_unique<gert::OpTilingContextBuilder>(); tilingCtxBuilder_->OpName("default_name").OpType("default_type").IONum(1, 1); tilingCtxBuilder_->CompileInfo("default_compileinfo").PlatformInfo("default_platforminfo"); kernelCtxBuilder_->OpName("default_name").OpType("default_type").IONum(1, 1); }可以归纳出,一次ContextBuilder()构造会在内存中建立以下状态:
| 初始化项 | 所属实现类 | 初值 / 说明 |
|---|---|---|
impl_ | ContextBuilder | 指向新建的ContextBuilderImpl(pimpl 指针) |
kernelCtxBuilder_ | ContextBuilderImpl | 新建gert::OpKernelContextBuilder,用于BuildKernelRunContext() |
tilingCtxBuilder_ | ContextBuilderImpl | 新建gert::OpTilingContextBuilder,用于BuildTilingContext() |
| OpName / OpType | 两个底层 Builder | 默认值"default_name"/"default_type",可被SetOpNameType覆盖 |
| 输入/输出数量(IONum) | 两个底层 Builder | 默认(1, 1),可被NodeIoNum/IrInstanceNum覆盖 |
| CompileInfo / PlatformInfo | tilingCtxBuilder_ | 占位字符串"default_compileinfo"/"default_platforminfo",可被CompileInfo/PlatformInfo/AddPlatformInfo覆盖 |
errFlag_ | ContextBuilderImpl | false(成员声明处初始化),后续AddInputTd等步骤出错时置位 |
inputNum_/outputNum_ | ContextBuilderImpl | 0(成员声明处初始化) |
从源码结构看,这一设计的意义在于:构造函数保证对象诞生时即处于“可直接链式配置、或直接 Build 出最简默认 Context”的合法状态,所有 setter 都是对默认值的逐步细化。ContextBuilderImpl中另有dependTensorsData_/dependOutputTensorsData_两个std::unordered_map<int32_t, std::unique_ptr<uint8_t[]>>成员(见 context_builder_impl.h),用于暂存AddInputTd/AddOutputTd创建的张量数据,默认构造时为空容器,按需填充。
对象语义:不可拷贝、不可移动
context_builder.h 中还显式删除了两个移动操作:
ContextBuilder(ContextBuilder &&kernelRunContextBuilder) = delete; ContextBuilder &operator=(ContextBuilder &&kernelRunContextBuilder) = delete;同时由于impl_是std::unique_ptr,隐式拷贝构造/拷贝赋值天然被删除。也就是说:ContextBuilder是一个非可移动、非可拷贝的对象,只能通过默认构造创建、以引用方式在链式调用中传递(这正是全部 setter 返回ContextBuilder&的原因)。实际使用时应让链式表达式直接绑定到同一个临时对象上,例如context_ascendc::ContextBuilder().SetOpNameType(...)...BuildTilingContext(),或将其作为局部变量声明后复用,而不能“存一份副本”或“移动进容器”。
典型使用方式:构造函数作为链式调用的起点
官方简介文档给出的两种构造范式(见 intro.md)都以context_ascendc::ContextBuilder()的临时对象构造作为链条起点:
// 构造 KernelContext(验证 TilingParse) auto kernelContextHolder = context_ascendc::ContextBuilder() .Inputs(...) .Outputs(...) .BuildKernelRunContext(); gert::KernelContext* tilingParseContext = kernelContextHolder->GetContext<gert::KernelContext>(); // 构造 TilingContext(验证 Tiling 函数) auto tilingContextHolder = context_ascendc::ContextBuilder() .SetOpNameType(..., ...) .NodeIoNum(...) .IrInstanceNum(...) .AddInputTd(...) .AddOutputTd(...) .AddAttr(...) .BuildTilingContext(); gert::TilingContext* tilingContext = tilingContextHolder->GetContext<gert::TilingContext>();这里的...参数可以参照仓库中的真实单测 test_tiling.cpp 补全,该用例完整走了一遍“构造 → 配置 → BuildTilingContext”的链路:
auto holder = context_ascendc::ContextBuilder() .NodeIoNum(1, 1) // 1 个输入、1 个输出 .IrInstanceNum({1}) // 每个输入的 IR 实例数 .SetOpNameType("tmpName", "tmpType") // 算子名 / 算子类型 .AddInputTd(0, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND, input_shape, file_path) // 输入 0:shape + bin 数据文件 .AddOutputTd(0, ge::DT_FLOAT16, ge::FORMAT_ND, ge::FORMAT_ND, output_shape) .TilingData(param.get()) // 由 gert::TilingData::CreateCap 分配 .Workspace(ws_size) // 由 gert::ContinuousVector 提供 .BuildTilingContext();两个值得注意的实现细节(来自 context_builder_impl.cpp):
- 出错即“静默失败”的 errFlag 机制:
AddInputTd/AddOutputTd在张量尺寸非法、bin 文件读取失败、内存分配失败等场景下不会抛异常,而是将errFlag_置true并记录日志;随后BuildTilingContext()检测到errFlag_时打印"Build Context Error!"并返回nullptr(见 BuildTilingContext 实现)。因此单测中对失败路径的断言是EXPECT_EQ(holder, nullptr)——判断 Build 是否成功,必须检查返回值是否为空指针。 - 构造函数与 Build 的配对关系:
ContextBuilderImpl构造时创建的kernelCtxBuilder_与tilingCtxBuilder_分别服务于两条 Build 路径。BuildKernelRunContext()只依赖Inputs/Outputs设置;BuildTilingContext()则消费NodeIoNum/IrInstanceNum/AddInputTd/AddOutputTd/AddAttr/CompileInfo/PlatformInfo/TilingData/Workspace的累计状态,最终通过tilingCtxBuilder_->Build()得到gert::ContextHolder<gert::TilingContext>,并包装为std::shared_ptr<KernelRunContextHolder>返回,由 holder 管理 Context 及其张量数据的生命周期。
小结
ContextBuilder()是无参默认构造,无参数、无额外约束(相对文档所述),是ContextBuilder唯一的对象创建入口。- 其“初始化数据成员”的实质是:分配 pimpl 指针
impl_,并在ContextBuilderImpl内建立gert::OpKernelContextBuilder与gert::OpTilingContextBuilder两个底层构建器,同时写入default_name/default_type/IONum(1,1)等可覆盖的默认值(context_builder_impl.cpp#L58-L66)。 - 对象不可拷贝、不可移动,只能就地声明并以返回值引用进行链式配置;
Build*系列接口以空指针表示失败,调用方需显式判空。 - 深入开发时,可结合 Tiling_tuning 文档目录 中
ContextBuilder各 setter 页面(如 AddInputTd、BuildTilingContext)与 OpTilingRegistry 一起使用,并在 tests/api/utils/tiling/test_tiling.cpp 中参考完整调用链与错误路径的验证方式。
- 人工智能
- 深度学习
- 算子库
- CANN
- Ascend
【免费下载链接】asc-devkit
本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。
相关推荐
asc-devkit 中 ContextBuilder 全解析:手动构造 TilingContext 与 KernelRunContext 验证算子 Tiling 函数
asc devkit 中 ContextBuilder 全解析:手动构造 TilingContext 与 KernelRunContext 验证算子 Tilin
人工智能深度学习算子库CANNAscendasc-devkit Tiling 调优指南:ContextBuilder::AddAttr 接口详解与 TilingContext 属性构造实战
asc devkit Tiling 调优指南:ContextBuilder::AddAttr 接口详解与 TilingContext 属性构造实战 在 asc
人工智能深度学习算子库CANNAscendasc-devkit Tiling 调测实践:ContextBuilder::AddInputTd 输入 Tensor 描述接口详解
asc devkit Tiling 调测实践:ContextBuilder::AddInputTd 输入 Tensor 描述接口详解 在算子 Tiling 函数
人工智能深度学习算子库CANNAscend
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考