Closed
[Requirement|需求建议]: 【社区任务】feat/RemainderTensorTensor算子AscendC A2/A3 内存优化 #2895
RandolphJiangAdd on Aug 27Closed at Oct 9
Aug 27 Associated pull request
陈思
Aug 27 Comments:
Aug 27 Comments:
/assign @RandolphJiang


Aug 27 Assign RandolphJiang as responsible person
liuchenghao
Oct 5 Comments:
Oct 5 Comments:
【Multica 同步】状态:backlog(待认领);说明:本算子任务已同步至 Multica「CANN 算子开发迁移」项目跟踪(社区已有认领人,Multica 侧承接前会先核对社区进展,避免重复实现);详情:AI4ME-57


Oct 9 Close issue
Oct 9 Added label: resolved
Thanks for sending an requirement! Please fill in the following template to help quickly solve your problem.
需求背景(required)
需求来源
基于 TBE FloorMod 历史版本,使用 Ascend C 对
aclnnRemainderTensorTensor进行内存与性能优化。原 ACLNN 在调用 FloorMod 前把输入广播到输出 shape,产生随广播结果线性增长的 GM 中间 Tensor。社区任务
要求在保持 remainder 语义、六种 dtype、公共 ABI 和 view/inplace 行为不变的
前提下,使 NPU/GPU 峰值内存差距小于 5%,且性能不低于原算子。本设计采用
stride-aware FloorMod,在 Kernel 内完成广播寻址。
对于可广播的
self、other,逐元素语义为:非零结果与
other同号;浮点保持 NaN、Inf、零除数和有符号零行为,整数避免零除数或
INT64_MIN / -1的未定义行为。原 ACLNN Tensor-Tensor 主链如下。这里的
BroadcastTo是输出规模的 GM Tensor,也是本任务需要消除的对象。
flowchart LR A["self"] --> C["Contiguous / Cast"] B["other"] --> D["Contiguous / Cast"] C --> E["BroadcastTo(outputShape)"] D --> F["BroadcastTo(outputShape)"] E --> G["FloorMod"] F --> G G --> H["Cast / Squeeze / ViewCopy"] H --> I["out"]设两输入元素数为
N1、N2,广播输出元素数为N,计算 dtype 字节数为S。原路径的逻辑额外内存近似为:M_result为 FloorMod 私有结果,M_adapt为 Contiguous/Cast/Squeeze/ViewCopy,M_fixed为 executor 固定项。只缩小 UB 或优化 Broadcast Kernel 无法消除两个N*S级对象。TBE 的
tbe.broadcast是 DSL 逻辑广播,不等同于上述 ACLNNBroadcastToGMTensor;本任务消除后者。
背景介绍
aclnnRemainderTensorTensor算子实现优化
本任务面向 Atlas A2/A3 训练系列产品,在不改变公共 ACLNN ABI、输出语义和原有
功能的前提下,将广播寻址融合到 FloorMod Kernel。实现中删除 ACLNN 侧
BroadcastTo(outputShape)形成的全尺寸 GM 中间 Tensor,使同条件 NPU/GPUDevice 峰值内存差距小于 5%;性能设计删除 Broadcast 任务和 GM 往返,
并按访问模式选择搬运路径。
aclnnRemainderTensorTensor算子TBE实现现状分析
TBE算子源码路径:
${ASCEND_INSTALL_PATH}/opp/built-in/op_impl/ai_core/tbe/impl/ops_legacy/dynamic/floor_mod.py静态TBE算子源码路径:
${ASCEND_INSTALL_PATH}/opp/built-in/op_impl/ai_core/tbe/impl/ops_legacy/floor_mod.py算子原型路径:
${ASCEND_INSTALL_PATH}/opp/built-in/op_graph/inc/elewise_calculation_ops.h算子信息库路径:
${ASCEND_INSTALL_PATH}/opp/built-in/op_impl/ai_core/tbe/config/ascend910b/aic-ascend910b-ops-info-legacy.json910B kernel config路径:
${ASCEND_INSTALL_PATH}/opp/built-in/op_impl/ai_core/tbe/kernel/config/ascend910b/ops_legacy/floor_mod.json1. 算子原型与能力边界
REG_OP(FloorMod) .INPUT(x1, TensorType({DT_INT32, DT_INT64, DT_FLOAT, DT_FLOAT16, DT_DOUBLE, DT_BF16})) .INPUT(x2, TensorType({DT_INT32, DT_INT64, DT_FLOAT, DT_FLOAT16, DT_DOUBLE, DT_BF16})) .OUTPUT(y, TensorType({DT_INT32, DT_INT64, DT_FLOAT, DT_FLOAT16, DT_DOUBLE, DT_BF16})) .OP_END_FACTORY_REG(FloorMod)kernel_name、impl_modecheck_supportedhigh_precision返回 false,可转 AICPU2. TBE 整体流程图
flowchart TD A["dynamic.floor_mod(x1, x2, y, kernel_name, impl_mode)"] --> B["读取 x1/x2 dtype"] B --> C["api_check_support(tbe.dsl.vmod, int64)<br/>形成 check_list"] C --> D["para_check.check_dtype<br/>检查两输入 dtype 相同"] D --> E["classify([x1,x2], ELEWISE_WITH_BROADCAST)"] E --> F{"遍历 classify 子图"} F --> G["tbe.compute()"] G --> H["shape_util.variable_shape"] H --> I["tvm.placeholder(input_data_x/y)"] I --> J["floor_mod_compute(..., impl_mode)"] J --> K["tvm.target.cce()"] K --> L["tbe.auto_schedule(res)"] L --> F F -->|完成| M["config: name + tensor_list"] M --> N["tbe.build(schedules, config)"] O["check_supported"] --> P["从 op_context 读取 FloorMod impl_mode"] P --> Q{"dtype=FP32 且 high_precision?"} Q -->|是| R["return false: 交由 AICPU"] Q -->|否| S["return true"]静态入口按
check_shape -> broadcast_shapes -> refine_shapes_for_broadcast -> placeholder -> compute -> auto_schedule -> build执行,仅声明 FP16/FP32/INT32;动态入口以
classify + variable_shape覆盖动态广播。3. TBE Compute 细化流程图
flowchart TD A["floor_mod_compute"] --> B["shape_util.broadcast_shapes"] B --> C{"dtype == int64?"} C -->|是| D["tbe.broadcast(x1, shape)"] D --> E["tbe.broadcast(x2, shape)"] E --> F["tbe.vmod(x1, x2)"] C -->|否| G{"impl_mode / dtype"} G -->|high_performance<br/>或 FP16/FP32| H["_mod(x1, x2)"] G -->|非 high_performance<br/>且 dtype 非 FP16/FP32| I["broadcast(2^24)"] I --> J["_mod(x1, 2^24)"] J --> K["_mod(2^24, x2)"] K --> L["vmul + vadd 后再次 _mod(..., x2)"] H --> M{"910B/910_93 且非 INT32?"} M -->|否| Z["返回 res"] M -->|是| N["broadcast one/zero/inf;再次 broadcast x1/x2"] N --> O["vabs + vcmp + vsel<br/>x2 为 Inf 时选择 x1"] O --> P["vabs + vcmp + vsel<br/>x1 为 Inf 时选择 NaN"] P --> Q["vsignbit + vcmp + vsel<br/>非零异号时 res += x2"] Q --> Z F --> Z L --> Zflowchart LR A["_mod(x1, x2)"] --> B{"支持 FP32 vdiv?"} B -->|是| C["cast_to FP32"] B -->|否| D["保留原 dtype"] C --> E["tbe.broadcast 两输入"] D --> E E --> F["vdiv"] F --> G["floor;按 API 能力选择 FP32/原 dtype/FP16"] G --> H["vmul quotient*x2"] H --> I["vsub x1-product"] I --> J["必要时 cast 回输入 dtype"]aclnnRemainderTensorTensor算子功能分析
aclnnRemainderTensorTensor对两个 Tensor 执行逐元素 floor modulus。输入 shape按尾维对齐并遵循广播规则,输出 shape 为广播结果;输入经过类型推导后使用共同
计算 dtype。优化前后的数学语义、异常处理、非连续 Tensor 适配和 inplace 约束
保持不变,仅改变广播数据的产生方式。
selfotherself,并满足类型推导规则self可广播out需求分析(required)
需求描述
使用 Ascend C 优化
aclnnRemainderTensorTensor的 Tensor-Tensor 广播路径。目标Kernel 根据输出坐标和输入 stride 直接读取紧凑输入,不再预先生成输出 shape
大小的广播 Tensor;结果与 PyTorch
torch.remainder及原算子一致,完整调用链性能不低于原算子,同条件 NPU/GPU Device 峰值内存差距小于 5%。
需求拆解
外部组件依赖
不涉及额外外部组件依赖。
内部适配模块
InferShape4Broadcast需求模块设计
算子原型
x1x2x1x1可广播yx1属性:
公共
aclnnRemainderTensorTensor与aclnnInplaceRemainderTensorTensor两段式 ABI 不变。输入可由 ACLNN promotion到共同计算 dtype;FloorMod 的两个输入和输出保持同 dtype,输出为广播 shape。
设计硬约束
算子支持型号
Atlas A2/A3 训练系列产品(
ASCEND910B/ASCEND910_93)。代码侧FloorModAICore 配置使用ascend910b,Ascend910_93与Ascend910B同属 DAV_2201 架构,按同一任务平台 route 处理。详细设计(required)
算子分析
数学公式
对于广播后的每个逻辑位置,计算公式为:
outi=selfi−⌊otheriselfi⌋×otheri
非零结果与
other同号。浮点路径需处理 NaN、Inf、零除数和有符号零;整数路径需规避除数为 0 及
INT64_MIN / -1的未定义行为。支持数据类型
self/other/out支持形状
self与other从尾维对齐,每个对齐维度相等或至少一侧为 1。outshape 必须等于两个输入的广播结果。算子实现
使能方式
实现方案
本次任务代码位于
experimental/math/floor_mod,核心文件与职责如下:op_api/aclnn_remainder.cppop_api/floor_mod.cppFloorModL0 封装,提供无输出和有输出 overload,并固定加入 AI Core launcherop_host/floor_mod_def.cppop_host/floor_mod_infershape.cppInferShape4Broadcast推导输出 shapeop_host/floor_mod_tiling.cppop_kernel/floor_mod.cpp/op_kernel/floor_mod.hop_kernel/floor_mod_tiling_data.h/floor_mod_tiling_key.h1. 候选方案与选择
BroadcastTo,只优化其 KernelN*S级 GM 对象仍存在N*S降为 tile选择“不落盘 Broadcast”,而非依赖 inplace 才节省内存:out-of-place 可直接写
用户
out,公共 inplace 只是把selfRef作为合法输出的一种形式;两者复用同一套 stride-aware Host/Kernel。
flowchart TD A["是否消除 N*S 级 GM Broadcast?"] --> B{"Broadcast 是否仍物化?"} B -->|是| C["不能满足根因优化"] B -->|否| D{"是否改变公共 out-of-place 语义?"} D -->|是| E["拒绝强制 inplace"] D -->|否| F{"是否新增分块 GM workspace 往返?"} F -->|是| G["保留为非首选方案"] F -->|否| H["选择 stride-aware FloorMod"]2. 总体数据流
flowchart TD A["self / other / out"] --> B["CheckParamsTensorTensor"] B --> C["promotion + Contiguous"] C --> D{"910B/910_93 且六种 dtype?"} D -->|是| E["不调用输出规模 BroadcastTensor"] D -->|否| F["兼容 route 按既有逻辑适配"] E --> G["FloorMod L0: AI Core launcher"] G --> H["FloorModTilingForGe"] H --> I["shape/stride/mode/block/UB tiling"] I --> J["FloorModKernelImpl"] J --> K["GM 原输入 -> UB tile -> y"] K --> L{"可直接写 out?"} L -->|是| M["完成"] L -->|否| N["Cast / Squeeze / ViewCopy"] N --> M F --> L3. Route 硬约束
IsNativeBroadcastFloorModType仅在ASCEND910B/ASCEND910_93且 promotiondtype 属于 BF16、FP16、FP32、INT32、INT64、DOUBLE 时返回 true。native route
不调用
BroadcastTensor。FloorMod的有输出和无输出 overload 均调用FloorModAiCore;FloorModAiCpu仅为兼容显式调用者保留,不被这两个 overload选择。
rank-0 输入在
InitializeTensor中通过BroadcastTo([1])规范为单元素 Tensor。这是 O(1) 的 shape 适配,不是任务要消除的输出规模 Broadcast;内存归因和 route
检查必须区分二者。
flowchart TD A["ExecRemainderTensorTensorGetWorkspaceSize"] --> B{"empty input?"} B -->|是| Z["返回空 executor"] B -->|否| C["PromoteType"] C --> D{"IsRegBase && dtype != DOUBLE?"} D -->|是| E["RegBase 既有 L0 FloorMod route"] D -->|否| F["InitializeTensor 两输入"] F --> F0{"rank == 0?"} F0 -->|是| F1["BroadcastTo([1]),仅 1 element"] F0 -->|否| G["保持连续 Tensor"] F1 --> G G --> H{"IsNativeBroadcastFloorModType?"} H -->|否| I["BroadcastTensor 兼容适配"] H -->|是| J["跳过输出规模 BroadcastTensor"] E --> K["l0op::FloorMod"] I --> K J --> K K --> L["FloorModAiCore / AI Core launcher"]flowchart LR A["910B/910_93 Tensor-Tensor"] --> B{"promotion dtype"} B -->|FP16/BF16/FP32| C["FloorMod<T> AI Core"] B -->|INT32| D["FloorMod<int> AI Core"] B -->|INT64| E["FloorMod<int64_t> AI Core"] B -->|DOUBLE| F["FloorModDouble AI Core"] C --> G["禁止 AICPU / 禁止全尺寸 BroadcastTo"] D --> G E --> G F --> G4. ACLNN 输出与 inplace 语义
RemainderMainProcess在无需 scalar squeeze、计算 dtype 等于输出 dtype 且out连续时,调用有输出 overload 直接写用户out。其他场景由 FloorMod 产生私有结果,再执行必要的 Cast、Squeeze、ViewCopy。全尺寸 Broadcast 输入中间
Tensor 在两类输出路径中均为 0;私有结果和输出适配 Tensor 按实际场景保留。
inplace 校验要求广播结果 shape 等于
selfRefshape,禁止扩展selfRef。各core 只写不重叠区间,tile 在写回前已将两个输入搬入 UB,因此 API 声明的
y == selfRef读后写关系成立。非连续、dtype 适配或 squeeze 场景必须走私有结果与 ViewCopy,不能强行地址复用。
flowchart TD A["aclnnInplaceRemainderTensorTensor"] --> B["out = selfRef"] B --> C["CheckBroadcastShape(..., isInplace=true)"] C --> D{"broadcastShape == selfRef.shape?"} D -->|否| X["返回参数错误"] D -->|是| E["Contiguous / promotion"] E --> F{"连续、同 dtype、无需 squeeze?"} F -->|否| G["私有 result -> ViewCopy(selfRef)"] F -->|是| H["每 core 独占输出区间"] H --> I["CopyIn x1/x2 到 UB"] I --> J["Compute"] J --> K["CopyOut 同一 selfRef 区间"]3.2.1 host侧设计:
1. InferShape、dtype 与 format
OpDef 为三端声明相同的六种 dtype、ND format 和动态 shape/rank;
InferShape4Broadcast推导输出。Tiling 再次校验描述符 dtype 一致、rank 不超过8、输入可广播、输出 storage shape 等于广播结果,并用
CheckedMultiply防止元素数或 stride 乘积回绕。
2. Host Tiling 总流程
flowchart TD A["TilingPrepare4FloorModTiling"] --> B["PlatformAscendC"] B --> C["GetCoreNumAiv / GetCoreMemSize(UB)"] C --> D["FloorModTilingForGe"] D --> E["读取 x1/x2/y desc 与 storage shape"] E --> F["GetBroadcastInfo"] F --> G["ReadAlignedShape: 左侧补 1"] G --> H["逐维广播校验 + CheckedMultiply"] H --> I["ShapeEquals(y, broadcastShape)"] I --> J["CompressBroadcastInfo"] J --> K["CalcBroadcastStride: 广播维 stride=0"] K --> L["按 element count / tail stride 选择 mode"] L --> M["SetTilingKeyParams(dtype)"] M --> N{"非 DOUBLE crossed-row?"} N -->|是| O["GetCrossedRowResources"] N -->|否| P["基础 UB / 分核规划"] O --> Q{"资源合法?"} Q -->|否| R["mode = GENERAL"] Q -->|是| S["物理行、blockAlign、UB tmp 预算"] R --> P S --> P P --> T["FloorModCommonTiling"] T --> U{"crossed-row usableUbSize == 0?"} U -->|是| V["回到 GENERAL 并重算 tiling"] U -->|否| W["写 FloorModTilingData"] V --> W W --> X["SetBlockDim / SetTilingKey / workspace=0"]3. Broadcast metadata 与 mode
右对齐后,输入线性地址为:
相邻同广播状态维先压缩;mode 如下:
CONTIGUOUSX1_SCALAR/X2_SCALARTAIL_CONTIGUOUSGENERALflowchart TD A["压缩后的 shape/stride"] --> B{"x1Size=x2Size=outSize?"} B -->|是| C["CONTIGUOUS"] B -->|否| D{"x1Size==1 或 x2Size==1?"} D -->|是| E["X1_SCALAR / X2_SCALAR"] D -->|否| F{"x1TailStride=x2TailStride=1?"} F -->|是| G["TAIL_CONTIGUOUS"] F -->|否| H{"尾维/上一维构成交叉 stride?"} H -->|是| I["CROSSED_ROW"] H -->|否| J["GENERAL"]4. 分核与 UB 预算
常规请求核数为
ceil(totalElements/1024),不超过平台 AIV 核数;每核逻辑区间按
blockAlignElements分配,余量由tailDataCoreNum和lastCoreDataCount表达。非 32B crossed-row 且原请求核数大于 4 时改为约 2048逻辑元素/核,以摊薄物理补齐和队列开销;这不是固定两个 row-group/核。
UB 先扣除固定保留、tiling data 和
broadcastTmpSize,再按 dtype divider 计算单 tile 元素数并向 64 元素对齐。通用模板一核单 tile 时使用 queue depth 1,
多 tile 时使用 depth 2。DOUBLE 的 divider 65 是保守 Host 预算;其 Kernel 不
申请 FP32 工作 Tensor,buffered mode 仅初始化三组 depth-2
uint64_t输入输出队列。
broadcastTmpSize是 UB 临时区,不是 GM workspace。flowchart TD A["ubSize"] --> B["减 RESERVED_UB_SIZE + sizeof(tilingData)"] B --> C["减 crossed-row broadcastTmpSize"] C --> D["除 dtype ubDivider"] D --> E["向下对齐 64 elements"] E --> F{"perCore < usableUbSize?"} F -->|是| G["generic queue depth=1"] F -->|否| H["generic queue depth=2"] E --> I{"DOUBLE buffered mode?"} I -->|是| J["3 x uint64 queues, depth=2"] I -->|否| K["逐元素 stride path"]crossed-row 物理尾长为:
非对齐时以完整 row cycle 为 core 边界,保证 core 从可复用标量边界开始。INT64
用 UB-to-UB 扩展且
broadcastTmpSize=0;其他 dtype 对可复用 outer group 使用软件扩展,否则通过
GetBroadCastMaxMinTmpSize查询 UB 临时空间。5. Tiling data 契约与降级边界
usableUbSizeneedCoreNumSetBlockDim与 KernelcoreNum一致totalDataCountint64_t,各 core 区间并集等于[0,total)perCoreDataCount/tailDataCoreNum/lastCoreDataCountblockAlignElementsbroadcastTmpSizerank/outShape/x1Stride/x2StridebroadcastModeFloorModBroadcastMode公共 API/OpDef 拒绝非任务 dtype;Host 对非法 shape/rank/乘积返回 tiling 失败。
crossed-row 资源查询失败或 UB 容量为 0 时只降级到 stride-aware
GENERAL并重算 tiling,不允许改走 AICPU,也不允许恢复输出规模 Broadcast。DOUBLE 的
crossed-row 由
FloorModDouble逐元素 stride 寻址,同样保持 AI Core 和不落盘约束。
flowchart TD A["Host 生成 specialized mode"] --> B{"shape/资源/UB 合法?"} B -->|是| C["写 specialized tiling"] B -->|否且输入契约非法| D["tiling failed"] B -->|否且仅 specialized 资源不足| E["mode = GENERAL"] E --> F["按基础 divider/core 重新 tiling"] F --> G["AI Core stride-aware Kernel"] C --> G G --> H["workspace=0;无全尺寸 Broadcast"]3.2.2 kernel侧设计:
1. Kernel 入口、Init 与 Process
flowchart TD A["floor_mod(GM_ADDR x1,x2,y,workspace,tiling)"] --> B["GET_TILING_DATA_WITH_STRUCT"] B --> C["FloorModKernelImpl<DTYPE_X1,...>"] C --> D{"template dtype == DOUBLE?"} D -->|是| E["FloorModDouble::Init"] D -->|否| F["FloorMod<T>::Init"] E --> G["FloorModDouble::Process"] F --> H["ParseTilingData / InitBuffers"] H --> I["FloorMod<T>::Process"] G --> J["完成"] I --> Jflowchart TD A["FloorMod<T>::Process"] --> B["初始化 dtype 常量/工作 Tensor"] B --> C{"T == INT64?"} C -->|是| D["PrecheckInt64BroadcastRange"] C -->|否| E{"broadcastMode"} D --> E E -->|CONTIGUOUS| F["ProcessContiguous"] E -->|CROSSED_ROW| G["ProcessBroadcastCrossedRow"] E -->|其他 INT64 general + scalar tail pair| H["ProcessBroadcastInt64Rows 或 ProcessBroadcast"] E -->|其他| I["ProcessBroadcast"] F --> J["CopyIn -> Compute -> CopyOut"] G --> J H --> J I --> J2. 搬运、双缓冲和尾块
通用模板的
ProcessContiguous逐 tile 执行CopyIn -> Compute -> CopyOut。多 tile 时输入输出队列深度为 2,用于异步流水的资源解耦;通用路径按 tile 顺序处理,不执行 next-tile prefetch。单 tile 使用
depth 1。
DOUBLE 的
ProcessBuffered/ProcessTailBuffered先 enqueue 当前 tile,在循环内enqueue 下一 tile 后再计算当前 tile,是明确的预取式双缓冲。TAIL 模式始终在
尾行边界截断,防止连续 DMA 跨到非连续源地址。
flowchart LR A["FloorMod<T> tile i"] --> B["CopyIn"] B --> C["EnQue / DeQue"] C --> D["Compute"] D --> E["CopyOut"] E --> F["tile i+1"]sequenceDiagram participant GM as GM participant Q as DOUBLE depth-2 Queue participant V as RemainderBits GM->>Q: enqueue tile 0 GM->>Q: prefetch tile 1 Q->>V: compute tile 0 V->>GM: copyout tile 0 GM->>Q: prefetch tile 2 Q->>V: compute tile 1 V->>GM: copyout tile 1DataCopyPad负责不足 32B 的 GM/UB 尾块。所有 core 的输出逻辑区间不重叠,MTE3 只写真实逻辑元素;UB padding 不得写入下一逻辑行或相邻 core 区间。
3. General 与 crossed-row 路径
flowchart TD A["ProcessBroadcast"] --> B["GetBroadcastSegmentLength"] B --> C["截断到尾维边界 / maxDataCount"] C --> D["GetInputOffset(x1Stride/x2Stride)"] D --> E["CopyInBroadcast"] E --> F{"tail stride"} F -->|0| G["读取 seed,UB 内扩展"] F -->|1| H["连续 DataCopyPad"] F -->|其他| I["按当前 segment offset 搬运"] G --> J["Compute"] H --> J I --> J J --> K["CopyOut logical count"] K --> L{"remainingCount > 0?"} L -->|是| B L -->|否| M["结束"]flowchart TD A["ProcessBroadcastCrossedRow"] --> B{"边界和 core ownership 合法?"} B -->|否| Z["ProcessBroadcast"] B -->|是| C{"dtype / tail 对齐"} C -->|非 INT64 且 row bytes 非 32B| D["ProcessBroadcastSoftwareRows"] C -->|INT64 且 tail%4 != 0| E["ProcessBroadcastInt64Rows"] C -->|已对齐| F["CopyInBroadcastCrossedRow"] D --> G{"可复用 outer group?"} G -->|是| H["CopyInBroadcastSoftwareGroups"] G -->|否| I["CopyInBroadcastSoftwareRows"] E --> J["32B seed + CopyInt64Rows"] F --> K["连续尾行 + row scalar"] H --> L["physical aligned UB rows"] I --> L J --> L K --> M["Compute"] L --> M M --> N["CopyOut logical tail,裁掉 padding"]4. dtype 计算设计
2^24拆分%+ 符号修正<2^24、零除、MIN/-1flowchart TD A["FP16/BF16/FP32 tile"] --> B{"FP16/BF16?"} B -->|是| C["Cast x1/x2 -> FP32"] B -->|否| D["使用 FP32 输入"] C --> E["Div -> Floor -> Mul -> Sub"] D --> E E --> F["abs(x2)==Inf: select x1"] F --> G["abs(x1)==Inf: select NaN"] G --> H["rem*x2<0: rem += x2"] H --> I{"输出 FP16/BF16?"} I -->|是| J["Cast 回原 dtype"] I -->|否| K["FP32 output"]flowchart TD A["INT64 tile"] --> B{"高复用小输入已预检查?"} B -->|否| C["tile Cast + ReduceMax/ReduceMin"] B -->|是| D{"全部 abs < 2^24 且 divisor != 0?"} C --> D D -->|是| E["vector Div/Floor/Mul/Sub"] D -->|否| F["逐元素 INT64 %"] F --> G["显式处理 divisor=0 与 MIN/-1<br/>不执行非法 %"] G --> H["非零异号: remainder += divisor"] E --> I["INT64 output"] H --> IDOUBLE 不允许通过
Cast(DOUBLE -> FLOAT)缩窄。FloorModDouble解析sign/exponent/fraction,按指数差执行 mantissa 长除法;异号且余数非零时计算
divisor - remainder,再由Pack处理 normal、subnormal、零和 nearest-even。flowchart TD A["RemainderBits(xBits, yBits)"] --> B["解析 sign/exponent/fraction"] B --> C{"NaN / x=Inf / y=0?"} C -->|是| D["quiet NaN pattern"] C -->|否| E{"x == 0?"} E -->|是| F["返回与 y 同号的 zero"] E -->|否| G{"y == Inf?"} G -->|是| H["同号返回 x;异号返回 y"] G -->|否| I["Decode mantissa/exponent"] I --> J{"exponentDelta < 0?"} J -->|同号| K["保留 abs(x)"] J -->|异号| L["SubtractBits(abs(y), abs(x))"] J -->|否| M["mantissa long division"] M --> N{"异号且 remainder != 0?"} N -->|是| O["y.mantissa - remainder"] N -->|否| P["保留 remainder"] K --> Q["Pack normal/subnormal"] L --> R["guard bits + nearest-even"] O --> Q P --> Q D --> S["写 uint64 bit pattern"] F --> S H --> S Q --> S R --> S3.2.3 关键内存与性能方案
1. HBM/GM 生命周期
native path 的额外内存模型为:
直接写连续同 dtype 的用户
out时M_result_optional=0;非连续输出、Cast 或scalar squeeze 可保留一个私有结果。输入连续化也可能产生与原输入大小相关的
紧凑副本。设计保证不再产生与广播输出
N等大的输入展开对象。sequenceDiagram participant U as User Tensor GM participant A as ACLNN Adapt GM participant K as FloorMod Kernel participant O as Output GM Note over U,O: 原路径还存在 BroadcastTo self/other 的 N*S 生命周期 U->>A: 必要时 Contiguous / Cast A->>K: 原始紧凑输入地址 Note over K: shape/stride 为 O(rank),workspace=0 K->>K: 每 core UB tile 读取与计算 K->>O: 直接写 out 或私有 result O->>O: 必要时 Cast / Squeeze / ViewCopy2. HBM 与 UB 分离
flowchart LR subgraph HBM["HBM / GM 峰值统计"] A["用户输入输出"] B["必要的 Contiguous / Cast / ViewCopy"] C["可选 FloorMod result"] D["Broadcast intermediate = 0"] end subgraph UB["每 AIV core LocalMemory"] E["input queues"] F["output queue"] G["dtype work tensors"] H["crossed-row physical row / tmp"] end I["FloorModTilingData O(rank)"] --> E A --> E F --> AFloorMod Kernel 的 GM workspace 为 0。连续、同 dtype、direct-out 场景下,
完整 executor 只保留固定对齐项,归入
M_fixed,不承载广播数据且不随 shape或 dtype 增长;非连续输入、promotion、非连续输出或 rank-0 squeeze 场景仍可能
出现 Contiguous/Cast/ViewCopy 等必要适配对象,但这些对象按真实输入或输出适配
归因,不是输出规模的输入 Broadcast 展开。native route 中两个输入保持紧凑
storage,输出规模的 Broadcast GM 中间量为 0;最终是否满足 5% 峰值差距以自测
报告的 NPU/GPU Device peak 数据为准。
3. 性能设计
性能设计先消除两个 Broadcast task 和 GM 往返,再按访问规律选择 mode:连续和
尾连续保持大块 DMA,scalar/crossed-row 在 UB 复用,GENERAL 只对无法归类的
stride 做坐标反解。常规场景约 1024 元素/核,非对齐短行约 2048 元素/核;
FP/INT32 优先向量化,INT64 通过范围预检查扩大 exact-FP32 向量路径,DOUBLE
保持 AI Core 并对规则模式显式预取。
flowchart TD A["完整调用链性能"] --> B["先删除 BroadcastTo task + GM round trip"] B --> C{"访问模式"} C -->|连续/尾连续| D["大块 DMA + queue buffering"] C -->|scalar| E["一次读取 + UB 扩展"] C -->|crossed-row| F["row/group 复用 + 物理对齐"] C -->|general| G["尾段截断 + stride 反解"] D --> H{"dtype"} E --> H F --> H G --> H H -->|FP/INT32| I["vector core"] H -->|INT64| J["exact-FP32 vector / scalar modulo"] H -->|DOUBLE| K["bit-pattern + explicit prefetch"]支持硬件
任务书适配硬件为 Atlas A2/A3 训练系列产品。当前 OpDef 在
ascend910b配置下注册,A3 的Ascend910_93与 910B 属于同一 DAV_2201任务平台,按同一 native route 验证。
算子约束限制
self和other最多支持 8 维,shape 必须满足 Broadcast 规则。ASCEND910B/ASCEND910_93)。selfRefshape,不允许扩展selfRef。可维可测分析
精度标准/性能标准
torch.remainder/ 原算子一致,满足 AscendOpTest 默认阈值特性交叉分析
1. dtype 与 mode
%2. view、inplace 与结果对象
outout=selfRef,tile 先读 UB 后写回N*S输入展开[1]归一 + Squeeze3. 架构与 route
FloorModDoubleAI Core验证方法与判定口径
按任务书判定:结果与 PyTorch/原算子一致,精度满足 AscendOpTest 默认阈值,
完整调用链性能不回退;内存验收以同条件 NPU/GPU Device 峰值差距小于 5% 为
通过标准。设计文档证明 native route 不再包含输出规模 Broadcast GM 中间量,
自测报告需给出实际 peak 数据完成闭环。
峰值统计覆盖完整 executor 生命周期;UB 不计入 HBM,固定 512B executor
对齐项单列。native route 的峰值模型不包含两个
N*S输入展开对象。flowchart TD A["同 shape / dtype / API 语义"] --> B["PyTorch / 原算子功能参考"] A --> C["原 ACLNN 完整调用链"] A --> D["优化 ACLNN 完整调用链"] A --> E["GPU peak memory"] B --> F["输出 / 特殊值 / dtype 比对"] C --> G["original latency + task sequence"] D --> H["optimized latency + task sequence"] D --> I["NPU Device peak"] H --> J["无全尺寸 BroadcastTo / 无 AICPU"] G --> K{"optimized <= original?"} H --> K I --> L["abs(NPU-GPU)/GPU"] E --> L L --> M{"gap < 5%?"}兼容性分析
稳定性与兼容性设计
稳定性设计不依赖跨核 workspace 或同步:每个 core 根据 tiling 计算独立输出区间,
只读输入 GM、只写所属输出区间;动态 shape 的所有地址参数由 Host 校验后下发。
specialized mode 的资源条件不成立时退回 GENERAL,而不是用未校验参数继续执行。
兼容性边界如下:
维护边界由四个契约组成:API route 决定是否允许物化 Broadcast;Host mode 决定
Kernel 数据搬运;tiling data 规定字段单位和 core ownership;dtype tiling key
决定计算实现。任何一层变更都必须沿依赖方向同步,不能只改局部常量或单个 case。
CheckedMultiply并校验输出 shapeGENERALtiling,保持 AI Core 和不落盘广播broadcastTmpSize与 Kernel 资源一一对应<2^24判定,超出范围使用标量%和符号修正MIN/-1%前显式分支,不下发非法运算IsNativeBroadcastFloorModType集中选路,FloorMod overload 固定使用FloorModAiCoreselfRefstorage 边界selfRefshapeflowchart LR A["shape / mode 变更"] --> B["GetBroadcastInfo"] B --> C["FloorModTilingData"] C --> D["Kernel Process 分发"] E["TQue / TBuf 变更"] --> F["UB divider / extra reserve"] F --> C G["dtype 变更"] --> H["OpDef + tiling key + Compute"] H --> F I["API route / output 变更"] --> J["内存生命周期 + alias"] J --> K["route / peak / latency 验收"] D --> K