asc-devkit Coordinate 辅助数据结构详解:基于 Coord、MakeCoord 与 Crd2Idx 的编译期坐标索引转换

📅 发布时间:2026/10/16 5:55:23
asc-devkit Coordinate 辅助数据结构详解:基于 Coord、MakeCoord 与 Crd2Idx 的编译期坐标索引转换
人工智能深度学习算子库CANNAscend【免费下载链接】asc-devkit本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言原生支持C和C标准规范主要由类库和语言扩展层构成提供多层级API满足多维场景算子开发诉求。项目地址https://gitcode.com/cann/asc-devkit点击查看免费下载Coordinate 是 asc-devkit昇腾 AI 处理器算子开发语言辅助数据结构中用于表示张量在各维度位置信息坐标值的核心类型本质上是一个元组tuple。在基于 LayoutShape Stride的张量编程模型中Coordinate 承担着逻辑坐标 → 内存线性索引的桥梁角色借助MakeCoord构造坐标、借助Crd2Idx结合 Shape 与 Stride 把坐标映射为一维内存位置索引 Index且全部计算都在编译期constexpr完成运行时零开销。读完本文你可以掌握 Coordinate 与 Layout、Index 三者的关系理解 Crd2Idx 的转换公式与 delinearize去线性化机制并能结合源码看懂各维度分支的底层实现在 AI Core 算子代码中独立完成坐标到地址偏移的编译期推导。Coordinate 的定位Coord、Layout 与 Index 的关系在 Coordinate 简介 中给出的定位是Coordinate 本质上是一个元组tuple用于表示张量在不同维度的位置信息即坐标值。Coordinate坐标和 Layout布局、Index内存位置索引之间存在紧密的关联从 Coordinate 到 Index 的转换Layout 定义了张量的形状Shape和各维度的步长Stride根据这些信息和给定的 Coordinate可以计算出该坐标在内存中的位置索引从 Index 到 Coordinate 的转换基于 Layout 中定义的形状和步长信息对于一个已知的内存位置索引通过相应的计算可以得到该索引对应的 Coordinate。Layout 简介 进一步说明了 Shape 与 Stride 的语义Shape 定义数据的逻辑形状如二维矩阵的行数列数或多维张量的各维大小Stride 定义各维度在内存中的步长即同维度相邻元素在内存中的间隔单位为元素与 Shape 的维度信息一一对应。例如一个二维矩阵 Shape 为(4, 2)、Stride 为(4, 1)表示矩阵有 4 行 2 列列方向相邻元素间隔 1 个元素、行方向相邻行的起始地址间隔 4 个元素。Coordinate 的 Crd2Idx 正是基于这套 Shape/Stride 信息完成坐标到线性地址的映射。Coordinate 的文档族由以下页面构成本文以 Coordinate 简介 为主体展开页面内容Coordinate 简介Coord 原型定义、模板参数、相关接口总览MakeCoord坐标构造函数的原型、参数与调用示例Crd2Idx坐标转索引的数学原理、原型、参数与调用示例原型定义与模板参数Coordinate 的原型定义如下template typename... Coords using Coord Std::tupleCoords...即Coord是Std::tuple见 Std::tuple 文档的类型别名坐标的每个维度作为一个 tuple 元素独立存储。模板参数说明参数名描述Coords表示输入数据类型的形参包参数个数范围为 [0, 64]。输入的数据类型支持size_t和Std::IntStd::Int 文档。这里有两点值得注意size_t与Std::IntN的双轨设计。Std::IntN是值注入类型value injection其值在编译期以模板参数形式存在因此由Std::Int构造的 Coord 可以参与纯编译期推导如constexpr上下文、作为其他模板类型的模板实参size_t则承载运行时才能确定的坐标值。二者在后续Crd2Idx的递归处理中走不同的分支这一点在源码分析部分展开。维度数上限 64。坐标维度数受形参包长度 [0, 64] 约束覆盖实际算子开发中的张量维度场景。从源码看上述别名与构造函数的实现位于 impl/basic_api/kernel_operator_coord.htemplate typename... Coords using Coord Std::tupleCoords...; template typename... Ts __aicore__ inline constexpr CoordTs... MakeCoord(Ts const... t) { return {t...}; }该文件是内部实现头文件公开使用应通过 basic_api/kernel_operator_intf.h 引入内部文件中也以#pragma message明确要求不得直接包含 impl 头文件Layout 相关的公开声明位于 kernel_operator_layout.h。MakeCoord坐标构造接口产品支持情况与 Crd2Idx 一致Ascend 950PR950DT 系列产品支持Atlas A3 系列产品支持Atlas A2 系列产品支持Atlas 200I/500 A2 推理产品不支持Atlas 推理系列产品 AI Core / Vector Core不支持Atlas 训练系列产品不支持功能说明将传入的数据打包成 Coord 数据结构。函数原型template typename... Ts __aicore__ inline constexpr CoordTs... MakeCoord(Ts const... t)参数说明参数名输入/输出描述t输入表示输入数据类型的形参包具体使用方法和约束说明同 Std::tuple。输入的数据类型支持size_t和Std::Int。返回值说明Coord 结构类型Std::tuple类型的别名。约束说明无。调用示例摘自 MakeCoord 文档auto blockCoordM Std::Int11{}; auto blockCoordN Std::Int12{}; auto coord AscendC::MakeCoord(blockCoordM, blockCoordN);从 impl/basic_api/kernel_operator_coord.h 的实现看MakeCoord的展开非常直接形参包t...被逐元素聚合初始化{t...}成对应的CoordTs...元组。返回值标注为constexpr意味着在编译期上下文中一个MakeCoord(Std::Int2{}, Std::Int3{})的结果本身就可以是常量表达式供后续模板元编程继续使用。Crd2Idx坐标到内存位置索引的转换功能说明摘自 Crd2Idx 文档Crd2Idx函数用于将多维坐标Coordinate通过布局Layout转换为内存位置索引Index这里的 Layout 包含了 Shape 和 Stride 信息。转换公式与基础示例对于一个布局 Layout其 Shape 为 (d₀, d₁, …, dₙ)Stride 为 (s₀, s₁, …, sₙ)Coordinate 为 (c₀, c₁, …, cₙ) 到线性索引 Index 的转换公式为Index c₀ * s₀ c₁ * s₁ c₂ * s₂ ... cₙ * sₙ例如对于 Shape(3, 4, 5)Stride(20, 5, 1)和 Coordinate(1, 2, 3)维度0: c₀ * s₀ 1 * 20 20 维度1: c₁ * s₁ 2 * 5 10 维度2: c₂ * s₂ 3 * 1 3 Index 20 10 3 33注意该公式对 Shape 本身没有直接使用——Shape 的作用体现在 Stride 的推导上以及维度不一致时的 delinearize 过程见下节。delinearize维度不一致时的去线性化当 Coordinate 维度和 Stride 维度不相同时可以采用去线性化delinearize的方法使得 Coordinate 维度和 Stride 维度相同再使用上述公式计算得到最终结果。对于一个 n 维数组形状为 (d₀, d₁, …, dₙ)线性坐标 c 对应的多维坐标 (c₀, c₁, …, cₙ)可以通过以下公式进行转换cₙ c % d₀ t₁ c / d₀ cₙ₋₁ t₁ % d₁ t₂ t₁ / d₁ …… c₀ tₙ₋₁ % dₙ文档中给出的一个典型场景是嵌套hierarchicalLayoutLayout(2, 4), (3, 5):(3, 6), (1, 24)即 Shape 与 Stride 均为两层嵌套元组Coordinate 为(11, 12)按列优先原则Crd2Idx 的结果为crd2idx delinearize(11, 12) * stride | ((11 % 2, 11 / 2), (12 % 3, 12 / 3)) * ((3, 6), (1, 24)) | ((1, 5), (0, 4)) * ((3, 6), (1, 24)) | 1 * 3 5 * 6 0 * 1 4 * 24 | 129总结上述过程完整的计算公式为Layout L (d₀, d₁, …, dₙ) : (s₀, s₁, …, sₙ) Coordinate c⃗ (c₀, c₁, …, cₙ) Crd2Idx(c⃗) Σᵢ₌₀ⁿ (sᵢ * cᵢ) (s₀, s₁, …, sₙ) · (c₀, c₁, …, cₙ)ᵀ Crd2Idx(x) Crd2Idx(delinearize(c, (d₀, d₁, …, dₙ))) Crd2Idx(c⃗) delinearize(c, (d₀, d₁, …, dₙ)) [ c mod d₀ (c // d₀) mod d₁ … (c // (d₀ · d₁ · … · dₙ₋₁)) mod dₙ ]这一机制的典型应用是块坐标场景一个大的逻辑维度被切分成 blockM × blockN 的块网格此时 Shape 与 Stride 都是二元嵌套元组外层是块内坐标、内层是块间坐标而坐标只是一个线性的块索引值delinearize 负责把线性值展开成与 Stride 同秩的多维坐标。函数原型与参数// Layout输入Coordinate转换为Index template typename CoordType, typename ShapeType, typename StrideType __aicore__ inline constexpr auto Crd2Idx(const CoordType coord, const LayoutShapeType, StrideType layout) // Shape和Stride输入Coordinate转换为Index template typename CoordType, typename ShapeType, typename StrideType __aicore__ inline constexpr auto Crd2Idx(const CoordType coord, const ShapeType shape, const StrideType stride)参数说明参数名输入/输出描述coord输入Std::tuple结构类型用于表示张量在不同维度上的坐标值。输入的数据类型支持size_t和Std::Int。layout输入输入的 Layout 对象。输入的数据类型支持size_t和Std::Int。shape输入Std::tuple结构类型用于定义数据的逻辑形状例如二维矩阵的行数和列数或多维张量的各维度大小。输入的数据类型支持size_t和Std::Int。stride输入Std::tuple结构类型用于定义各维度在内存中的步长即同维度相邻元素在内存中的间隔单位为元素与 Shape 的维度信息一一对应。输入的数据类型支持size_t和Std::Int。返回值说明返回根据 Coordinate 信息转换之后的索引值。约束说明输入参数需满足对应的数据类型要求。调用示例摘自 Crd2Idx 文档// Layout形式入参计算索引值 constexpr int M 11; constexpr int N 12; constexpr int blockM 13; constexpr int blockN 14; auto coord AscendC::MakeCoord(AscendC::Std::Int20{}, AscendC::Std::Int30{}); auto shape AscendC::MakeShape(AscendC::MakeShape(AscendC::Std::IntblockM{}, AscendC::Std::IntM/blockM{}), AscendC::MakeShape(AscendC::Std::IntblockN{}, AscendC::Std::IntN/blockN{})); auto stride AscendC::MakeStride(AscendC::MakeStride(AscendC::Std::IntblockN{}, AscendC::Std::IntblockM*blockN{}), AscendC::MakeStride(AscendC::Std::Int1{}, AscendC::Std::IntM*blockN{})); auto layout AscendC::MakeLayout(shape, stride); auto index layout(coord); // decltype(index)::value 590 index AscendC::Crd2Idx(coord, layout); // decltype(index)::value 590 // Shape和Stride形式入参计算索引值 auto blockCoordM AscendC::Std::Int11{}; auto blockCoordN AscendC::Std::Int12{}; auto baseShapeM AscendC::Std::Int13{}; auto baseShapeN AscendC::Std::Int14{}; auto basestrideM AscendC::Std::Int15{}; auto basestrideN AscendC::Std::Int16{}; auto coord AscendC::MakeCoord(AscendC::Std::Int0{}, blockCoordN); auto shape AscendC::MakeShape(AscendC::MakeShape(baseShapeM, baseShapeM), AscendC::MakeShape(baseShapeN, baseShapeN)); auto stride AscendC::MakeStride(AscendC::MakeStride(basestrideM, basestrideM), AscendC::MakeStride(basestrideN, basestrideN)); auto index AscendC::Crd2Idx(coord, shape, stride); // decltype(index)::value 192示例中体现了两个要点其一layout(coord)Layout 的operator()与Crd2Idx(coord, layout)结果等价其二当结果类型是Std::Int时索引值以decltype(index)::value的形式在编译期取用验证了整条推导链的编译期性质。源码级实现分析以上数学过程在 impl/basic_api/kernel_operator_coord.h 中通过模板元编程落地。该文件定义了AscendC命名空间下的 Coord 别名、MakeCoord、Product、TransformApply以及Crd2Idx的各重载值得逐层拆解。Crd2Idx 的三种形态分派对外暴露的Crd2Idx(coord, shape, stride)重载通过if constexpr按 coord / shape 是否为 tuple 分派到三个分支见 impl/basic_api/kernel_operator_coord.htemplate typename T, typename U, typename S __aicore__ inline constexpr auto Crd2Idx(const T coord, const U shape, const S stride) { if constexpr (Std::is_tuple_vT) { if constexpr (Std::is_tuple_vU) { // tuple tuple tuple static_assert(Std::tuple_size_vT Std::tuple_size_vU, Shape and Coord Mismatched Ranks); static_assert(Std::tuple_size_vT Std::tuple_size_vS, Stride and Coord Mismatched Ranks); return Crd2IdxTTT(coord, shape, stride, tuple_sequenceT{}); } else { // tuple int int static_assert(sizeof(T) 0, Invalid parameters, U is not tuple!); } } else { if constexpr (Std::is_tuple_vU) { // int tuple tuple static_assert(Std::tuple_size_vU Std::tuple_size_vS, Shape and Stride Mismatched Ranks); return Crd2IdxITT(coord, shape, stride, tuple_sequenceU{}); } else { // int int int return coord * stride; } } }tuple × tuple × tupleCrd2IdxTTTcoord、shape、stride 都是同秩元组逐维计算Std::getIs(coord) * Std::getIs(stride)后求和注意此分支对 shape 只做秩校验累加项来自坐标与步长的逐维乘积正对应公式Index Σ cᵢ·sᵢ见 impl/basic_api/kernel_operator_coord.h标量 × tuple × tupleCrd2IdxITTcoord 是单个线性值如块索引 11、12shape 与 stride 是嵌套元组走 delinearize 的递归展开标量 × 标量 × 标量最简单的coord * stride单项乘积作为递归的叶子情形。两个static_assert在编译期把Coordinate 与 Shape/Stride 秩不匹配这类错误拦截下来报错信息分别为Shape and Coord Mismatched Ranks、Stride and Coord Mismatched Ranks与文档输入参数需满足对应的数据类型要求的约束说明对应。delinearize 的递归实现与编译期优化Crd2IdxITT是 delinearize 公式的递归编码见 impl/basic_api/kernel_operator_coord.htemplate typename T, typename U, typename S, size_t I0, size_t... Is __aicore__ inline constexpr auto Crd2IdxITT( const T coord, const U shape, const S stride, Std::index_sequenceI0, Is...) { if constexpr (sizeof...(Is) 0) { // Avoid recursion and mod on single/last iter return Crd2Idx(coord, Std::getI0(shape), Std::getI0(stride)); } else if constexpr (Std::is_constant0, T::value) { return Crd2Idx(CoordImpl::IntZero{}, Std::getI0(shape), Std::getI0(stride)) (CoordImpl::IntZero{} ... Crd2Idx(CoordImpl::IntZero{}, Std::getIs(shape), Std::getIs(stride))); } else { // General case auto prod Product{}(Std::getI0(shape)); auto div coord / prod; auto mod coord % prod; return Crd2Idx(mod, Std::getI0(shape), Std::getI0(stride)) Crd2IdxITT(div, shape, stride, Std::index_sequenceIs...{}); } }对应关系是Product{}(shape[I0])计算prod d₀ · d₁ · …子元组内各维度的乘积mod coord % prod即 delinearize 的取模得当前维坐标div coord / prod即整除传递给下一层然后对剩余维度递归。文件顶部的CoordImpl::IntZeroimpl/basic_api/kernel_operator_coord.h专门服务于第二个分支当编译期已知坐标为Std::Int0时直接以零坐标展开各维并累加避免了无意义的除模运算——这是对块坐标为 0这类常见特例的编译期短路优化。Product与TransformApply则负责嵌套元组求积见 impl/basic_api/kernel_operator_coord.hTransformApply检测输入是否为 tuple是则通过TupleApplymake_index_sequence展开为逐元素映射再交给折叠函数MultipliesUnaryLeftFold做(… * t)的折叠相乘空元组按约定返回Std::Int1单位元标量整数直接返回不满足条件的类型以static_assert失败。这正是 Layout 简介 所强调的借助模板元编程技术在编译时完成计算和代码生成从而降低运行时开销的具体体现。Layout 重载只是形状步长的转接接受LayoutShapeType, StrideType的重载实现极为简洁见 impl/basic_api/kernel_operator_coord.htemplate typename T, typename U, typename S __aicore__ inline constexpr auto Crd2Idx(const T coord, const LayoutU, S layout) { return Crd2Idx(coord, layout.GetShape(), layout.GetStride()); }即取出GetShape()/GetStride()后转调 ShapeStride 重载与调用示例中layout(coord) Crd2Idx(coord, layout)的等价性一致。在算子开发中的典型使用方式将上述机制串起来一个典型的 AI Core 侧使用流程是用MakeShape/MakeStride配合Std::IntN或size_t描述张量的逻辑形状与内存步长必要时用MakeLayout组合成 Layout用MakeCoord给出目标位置的坐标可以是标量嵌套元组的块坐标调用Crd2Idx或layout(coord)得到Std::Int/整型形式的线性索引再将其换算为地址偏移用于取数或访存。由于MakeCoord与Crd2Idx均为__aicore__ inline constexpr整条坐标 → 索引链路可以在编译期完成当所有输入都是值注入类型Std::IntN时索引在编译期即为常量可直接嵌入后续模板参数或constexpr表达式当坐标来自运行时变量size_t时if constexpr分派会退化为运行期执行同样的乘加/除模逻辑语义保持一致。仓库的 tensor_api 相关测试如 test_tensor_api_data_structure_strict.cpp、test_tensor_api_data_structure_matrix.cpp中即包含对MakeCoord/Crd2Idx的调用验证可作为行为参照。注意事项与约束小结类型约束Coord 各维度元素仅支持size_t和Std::IntShape、Stride 同理。传入其他类型会触发实现中的static_assert如Invalid parameters、Invalid Product parameters。秩一致性coord 为元组时其元素个数必须与 shape、stride 的顶层元素个数一致否则编译期报错Shape and Coord Mismatched Ranks/Stride and Coord Mismatched Rankscoord 为标量时要求 shape 与 stride 同为元组且二者秩一致。维度范围Coords形参包个数范围 [0, 64]。产品支持MakeCoord、Crd2Idx支持 Ascend 950PR950DT、Atlas A3、Atlas A2 系列产品不支持 Atlas 200I/500 A2 推理产品、Atlas 推理系列产品AI Core 与 Vector Core及 Atlas 训练系列产品。头文件内部实现位于 impl/basic_api/kernel_operator_coord.h其中声明不得直接包含算子代码应通过 basic_api/kernel_operator_intf.h 等公开头文件使用AscendC::MakeCoord/AscendC::Crd2Idx。命名空间与符号相关接口均位于AscendC命名空间调用时写作AscendC::MakeCoord、AscendC::Crd2IdxStd::tuple、Std::Int等基础类型定义见 Utils-API 标准库文档。小结Coordinate 以元组即坐标的极简设计配合MakeCoord的构造与Crd2Idx的坐标-索引转换构成了 asc-devkit 中 Layout 编程模型的基础坐标系Shape/Stride 描述内存布局Coordinate 表达逻辑位置Index 给出访存偏移。其核心价值在于整条转换链通过模板元编程在编译期完成——秩校验、delinearize 的除模展开、嵌套维度求积全部在编译期分派与折叠既保证了运行时无额外开销也把布局不一致这类错误提前到了编译期暴露。对于需要按块、按行、按张量维度推导访存偏移的算子开发掌握MakeCoordCrd2Idx的组合使用是编写正确 Layout 代码的前提。赞分享人工智能深度学习算子库CANNAscend【免费下载链接】asc-devkit本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言原生支持C和C标准规范主要由类库和语言扩展层构成提供多层级API满足多维场景算子开发诉求。项目地址https://gitcode.com/cann/asc-devkit点击查看免费下载相关推荐asc-devkit 算子开发 Coordinate 坐标数据结构的原理与实践Coord、MakeCoord 与 Crd2Idx 完整指南asc devkit 算子开发 Coordinate 坐标数据结构的原理与实践Coord、MakeCoord 与 Crd2Idx 完整指南 Coordinat人工智能深度学习算子库CANNAscendasc-devkit 中的 Layout 数据结构Shape/Stride 驱动的张量内存布局与坐标索引映射asc devkit 中的 Layout 数据结构Shape/Stride 驱动的张量内存布局与坐标索引映射 LayoutShape, Stride 是 C人工智能深度学习算子库CANNAscendAscend C Cast 数据类型转换实战基于 asc-devkit 实现 half 到 int32_t / int4b_t 精度转换Ascend C Cast 数据类型转换实战基于 asc devkit 实现 half 到 int32_t / int4b_t 精度转换 导读 本文以 asc人工智能深度学习算子库CANNAscend上一篇YTKNetwork 基础使用指南面向对象化 iOS 网络请求封装实战YTKNetworkConfig YTKRequest 全解析下一篇AlgoNote 题解深度剖析LeetCode 0898「子数组按位或操作」—— 动态规划 哈希集合的 O(n log C) 解法创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考