ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CANN opbase 基础库 CeilDiv 向上取整除法的实现原理与算子开发实践

CANN opbase 基础库 CeilDiv 向上取整除法的实现原理与算子开发实践 CANN opbase 基础库 CeilDiv 向上取整除法的实现原理与算子开发实践【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase导读CeilDiv向上取整除法是 CANN 算子库基础框架 opbase 中提供的基础数学工具函数之一用于在算子开发过程中将任意非负整数量按固定步长向上对齐到整数个块如计算分块数、核数、缓存行数等。本文以 docs/zh/api/op_common/math/CeilDiv.md 为主体结合 opbase 仓库内的头文件实现、单元测试与 tiling 源码完整讲解 CeilDiv 的函数原型、有符号/无符号两套实现差异、边界行为除数为 0、约束条件及其在算子 tiling 与数据切分中的典型用法。读完本文你将能在自己的算子 Host 侧与 Kernel 侧代码中正确使用 CeilDiv并理解其与 FloorDiv、CeilAlign 等配套工具函数的区别。功能说明CeilDiv 实现向上取整的除法即对x / y的结果向上取整向正无穷方向取整常用于给定总量与每份容量求最少需要多少份的计算场景。例如CeilDiv(5000, 4096)结果为 2含义是 5000 个元素按每份 4096 划分至少需要 2 份。函数原型与实现原理CeilDiv 在 opbase 中针对有符号整数和无符号整数分别提供了模板重载两者的原型如下对应 include/op_common/op_host/util/math_util.h// 有符号整型版本 template typename T auto CeilDiv(T x, T y) - typename std::enable_ifstd::is_signedT::value, T::type; // 无符号整型版本 template typename T auto CeilDiv(T x, T y) - typename std::enable_ifstd::is_unsignedT::value, T::type;两个重载通过std::enable_if结合std::is_signed/std::is_unsigned在编译期进行匹配因此调用时无需额外指定重载模板实参推导与 SFINAE 会自动选择对应实现。有符号版本处理负数的符号位逻辑有符号版本的实现如下include/op_common/op_host/util/math_util.htemplate typename T auto CeilDiv(T x, T y) - typename std::enable_ifstd::is_signedT::value, T::type { if (y ! 0 x ! 0) { const T quotient x / y; return (x % y ! 0 ((x ^ y) 0)) ? (quotient 1) : quotient; } return x; }关键点在于条件(x % y ! 0 ((x ^ y) 0))x % y ! 0判断是否能被整除能整除时商本身就是整数直接返回quotient(x ^ y) 0通过异或运算判断x与y的符号位是否相同。同号时异或结果符号位为 0 0表示商为正或零此时整数除法向零截断会少算小数部分需要quotient 1异号时商为负向零截断的结果恰好等于向上取整结果例如-5 / 2的整数除法为-2而ceil(-2.5) -2直接返回quotient。由此可以归纳有符号版本的取值规律x 与 y 的符号典型例子结果数学含义同号正/正CeilDiv(5000, 4096)2ceil(5000/4096)同号负/负CeilDiv(-5, -2)3ceil(2.5)异号CeilDiv(5, -2)、CeilDiv(-5, 2)-2ceil(-2.5)即该实现保证结果始终等于ceil(x / y)向正无穷方向取整且除法基于 C 的向零截断语义修正而来。无符号版本简洁的余数判断无符号版本无需处理符号实现更为直接include/op_common/op_host/util/math_util.htemplate typename T auto CeilDiv(T x, T y) - typename std::enable_ifstd::is_unsignedT::value, T::type { if (y ! 0 x ! 0) { const T quotient x / y; return (x % y ! 0) ? (quotient 1) : quotient; } return x; }无符号场景下x / y必为非负因此只要x % y ! 0余数不为 0就向上进位加 1否则直接返回商。除数为 0 的边界行为与常规除法不同CeilDiv在除数为 0 时不会崩溃if (y ! 0 x ! 0)分支外的兜底逻辑直接return x即返回被除数。同理当被除数x为 0 时也直接返回x0。这一设计让调用方在 tiling 计算中即使拿到未初始化的步长或容量值0也不会触发除零异常而是获得一个可继续参与计算的降级结果。这一点与 include/op_common/op_host/util/math_util.h 中FloorDiv的y 0 ? x : x / y处理策略保持一致。参数与返回值说明参数名输入/输出说明x输入被除数。y输入除数。返回值类型T返回向上取整的除法结果当除数为 0 时返回被除数x。返回值类型与入参类型一致模板参数T由调用时的显式模板实参或入参类型推导决定。约束说明类型约束x、y必须是相同类型的整型is_integral语义内的整数类型不包括 bool 类型模板的std::enable_if只对有符号/无符号整型生效传入浮点类型或 bool 将无法匹配到重载编译报错。同号除法的溢出风险从有符号实现代码可以推断当x为INT_MIN/LONG_MIN且y为-1时x / y本身会触发有符号整数溢出C 标准中为未定义行为该实现未对此做额外保护调用方应在传入极端最小值时自行规避。调用位置约束文档所述的 Host 侧版本位于Ops::Base命名空间使用时需包含头文件op_common/op_host/util/math_util.h例如#include op_common/op_host/util/math_util.h后通过using namespace Ops::Base;或Ops::Base::CeilDivT(...)调用参见 tests/op_common/ut/test_util.cpp。调用示例文档给出的关键示例原样保留供理解语义不可直接拷贝运行CeilDivint32_t(5000, 4096)运行结果为2。更多可直接验证的取值可参考仓库单元测试中的断言tests/op_common/ut/test_util.cppCeilDivint32_t(5000, 4096) // 2 CeilDivint32_t(130, 32) // 5 CeilDivint32_t(2500, 1024) // 3 CeilDivint32_t(999, 100) // 10 CeilDivint32_t(87, 20) // 5 CeilDivint32_t(std::numeric_limitsint32_t::max(), 8) // 268435456 CeilDivint64_t(std::numeric_limitsint64_t::max(), 8) // 1152921504606846976其中INT32_MAX / 8的结果268435456验证了余数非零必须进位的核心语义ceil(2147483647 / 8) 268435456。源码级验证单元测试opbase 在 tests/op_common/ut/test_util.cpp 中为数学工具函数提供了完整的 GoogleTest 单元测试其中testCeilDiv覆盖了普通整除、非整除进位以及 int32/int64 极值场景与文档中向上取整、除数为 0 返回被除数的语义完全一致。同文件中的testFloorDiv、testFloorAlign、testCeilAlign则分别验证了配套的向下取整除法、向下/向上对齐工具可作为理解整组数学工具函数行为差异的参照。在算子开发中的实际应用CeilDiv 在 opbase 中并不是孤立存在的工具函数它被大量用于算子的 tiling分块调度计算中以下是仓库内的几处典型落地场景。1. 分块数量与核数分配计算Reduce 算子 tiling在 src/op_common/atvoss/reduce/reduce_tiling.cpp 中CeilDiv 被用来计算总量 ÷ 每核负载 所需核数/块数uint64_t perCoreNum CeilDiv(unitA_.outer * unitR_.outer, compileInfo_-vectorCoreNum); uint64_t numBlocks CeilDiv(unitA_.outer * unitR_.outer, perCoreNum); uint64_t factorACntPerCore CeilDiv(unitA_.outer, numBlocks); uint64_t factorRCntPerCore CeilDiv(unitR_.outer, CeilDiv(numBlocks, unitA_.outer)); tilingData_-groupR CeilDiv(unitR_.outer, factorRCntPerCore);这里 CeilDiv 的核心价值在于外层迭代总数除以每份容量后余数非零时必须补一个块否则数据会被截断丢失。类似的用法还出现在缓存行切分CeilDiv(cacheSize, cacheLineShape)、维度切分CeilDiv(shape[i], ubFactorR)等逻辑中参见 reduce_tiling.cpp、reduce_tiling_batch_invariant.h。2. 数据等分切分工具SplitIntoEqualByParts / SplitIntoEqualByFactorsrc/op_common/op_host/util/math_util.cpp 中的两个数据切分函数直接以 CeilDiv 为核心实现把总长切分为尽量等长的若干子块splitResult.splitCount splitLen parts ? splitLen : parts; splitResult.splitFactor CeilDiv(splitLen, static_castint64_t(splitResult.splitCount)); splitResult.splitCount CeilDiv(splitLen, splitResult.splitFactor);以及splitResult.splitCount CeilDiv(splitLen, static_castint64_t(splitResult.splitFactor));这两处分别演示了 CeilDiv 的两种典型用法由份数求每份容量总量 ÷ 份数向上取整与由每份容量求份数总量 ÷ 容量向上取整是算子 Host 侧数据切分的通用模板。3. Kernel 侧AI Core的 CeilDiv 变体除了 Host 侧文档所述的模板重载opbase 还在 include/op_common/op_kernel/math_util.h 提供了标注__aicore__、可在 AI Core 内核中内联调用的版本template typename T __aicore__ inline T CeilDiv(T a, T b) { using type typename std::conditionalsizeof(T) sizeof(uint8_t) || sizeof(T) sizeof(uint16_t), uint32_t, uint64_t::type; type res (static_casttype(a) static_casttype(b) - 1) / static_casttype(b); return static_castT(res); }该变体采用经典的(a b - 1) / b公式并通过std::conditional将 8/16 位类型提升到uint32_t、其余提升到uint64_t后再计算以避免a b - 1在窄类型上溢出。与 Host 侧版本不同它面向 Vector/立方核内的轻量计算没有除 0 保护使用时应确保b 0。从源码结构看Host 侧与 Kernel 侧两套 CeilDiv 分别服务于算子开发的不同阶段Tiling 计算与内核计算。与配套工具函数的对比include/op_common/op_host/util/math_util.h 中 CeilDiv 与以下工具函数共同构成整型运算工具集选择时可按语义对照函数语义除数为 0 时的行为典型场景CeilDiv(x, y)ceil(x / y)向上取整返回x求最少块数/核数FloorDiv(x, y)floor(x / y)向下取整返回x求可完整放下的块数CeilAlign(x, align)ceil(x / align) * align向上对齐到 align 的倍数align 为 0 返回 0内存/缓冲对齐带溢出钳制超过类型最大值时返回最大值FloorAlign(x, align)x / align * align向下对齐到 align 的倍数align 为 0 返回 0缓冲区基址对齐计算其中CeilAlign内部正是调用CeilDiv实现include/op_common/op_host/util/math_util.h可见 CeilDiv 是整个对齐工具链的基石其边界行为除数/对齐值为 0 时不异常、向上对齐溢出时钳制到numeric_limitsT::max()也已在 tests/op_common/ut/test_util.cpp 中通过testCeilAlign覆盖验证。总结CeilDiv 是 CANN opbase 基础框架库中最常用的整型数学工具之一Host 侧通过有符号/无符号两套std::enable_if重载实现了向正无穷取整的除法语义并以除数为 0 返回被除数的兜底策略保证了 tiling 计算的健壮性Kernel 侧则提供基于(a b - 1) / b公式的__aicore__内联变体。无论是求分块数量、核数分配、缓存行切分还是实现数据等分切分工具CeilDiv 都承担着向上补一块这一不可缺失的语义建议算子开发者将其作为处理取整除法的首选工具并配合FloorDiv、CeilAlign、FloorAlign组成完整的对齐运算方案。【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表