已关闭
[RFC]: RoiawarePool3d 算子优化设计文档 #340
gcw_GF5DMRgv创建于 8月18日关闭于 9月18日
8月18日 添加了label:rfc
8月18日 修改了issue 的描述
8月18日 修改了issue 的描述
xiangyuming
8月18日 评论:
8月18日 评论:
/label add triaged


8月18日 添加了label:triaged
8月27日 修改了issue 的描述
8月27日 修改了issue 的描述
9月18日 添加了label:resolved
状态(Status): reviewing
作者(Authors): @gcw_GF5DMRgv
创建日期(Created): 2026-08-18
更新日期(Updated): 2026-08-18
**相关 Issue/PR:**https://gitcode.com/Ascend/DrivingSDK/issues/230
1. 概述
1.1 简介
本设计文档描述 DrivingSDK 中 RoiawarePool3d 自定义算子在 Ascend NPU 上的性能与健壮性优化。RoiawarePool3d 是 3D 目标检测(PointPillars、CenterPoint 等)中 ROI 特征提取的关键算子:对每个 3D 检测框内的点云,先经旋转局部坐标变换判定框内点,再将其划入
outx × outy × outz体素网格,最后对每个体素内的点特征执行 max 或 avg 池化,输出池化特征、argmax(最大特征对应点索引)与体素点索引表。1.2 动机
使用场景:RoiawarePool3d 在 3D 检测模型的 ROI 特征提取阶段被高频调用。模型对每个候选框执行"框内点判定 → 体素划分 → 池化",框数量级为数百至数千、框内点数万级、通道数 64~512,且每个框的体素网格固定为
outx×outy×outz。算子耗时主要由逐点几何判定与逐体素池化构成。当前痛点(来自
master基线与roi分支的代码对比分析):check_pt_in_box3d对每个点执行一次Cos/Sin(每次仅 1 元素向量调用 + V→S 同步),同一 box 的ptsNum个点重复计算同一角度,冗余且串行化严重。GetValue读取坐标、标量分支判定框内、标量计算体素编号,循环内还伴随多次PipeBarrier<PIPE_ALL>;点云全部加载到 UB 后,Vector 单元几乎闲置。collect_inside_pts_for_box3d对每个框内点都做一次"读体素表 → 计数 → 写回"的完整 GM 往返(DataCopy+PipeBarrier),同一体素被多次重复读写。compute_featrue以channelIdx为外层循环,逐 channel 用标量GetValue比较更新 argmax;Max/Add向量指令只在channelIdx == 0执行一次,其余channelNum - 1个 channel 全部退化为标量读写。SetBlockDim(coreNum)并用coreNum分核,当boxNum < coreNum时大量 Core 空转;且无 UB 容量校验,大输入存在 UB 溢出风险。boxIdx * outx * outy * outz * ...等乘法在 32 位域完成,大 box 数下可能溢出。不做此提案的影响:在框数多、通道数大的场景下,逐点标量判定与逐 channel 标量池化会放大标量指令占比与 GM 往返次数,使 RoiawarePool3d 成为 ROI 特征提取阶段的可观热点;同时分核缺陷与 UB 无校验会带来空转与稳定性风险。
1.3 目标
目标:
Cos/Sin(替代逐点标量计算)。GatherMask压紧有效点,再按体素一次性写出索引,消除逐点读改写。Max/Add/Muls按channelNum整行执行,argmax 用Compare+Select向量化更新(保持 first-point-wins)。validCount == 0直接Duplicate默认值写回。min(coreNum, boxNum);GM 偏移全部 64 位计算。非目标:
roiaware_pool3d_grad的对外行为。2. 用例分析
2.1 功能用例
outx×outy×outz体素,逐体素池化mode=0:体素内特征逐 channel 取最大,argmax 记录最大特征对应的点索引(首个最大者优先)mode=1:体素内特征逐 channel 取平均,argmax 统一为 0max_pts_per_voxel - 1时截断pts_idx_of_voxels[..., 0]为计数,未用 slot 保持 02.2 性能指标
ptsNum × coreRoiNum次标量 Cos/Sin 降为 2 次整行向量调用(Cos、Sin 各 1)Max/Add/Muls按整行 channelNum 执行,argmax 向量化U = min(coreNum, boxNum),消除空转核2.3 DFX 要求
mode、max_pts_per_voxel、out_size语义不变2.4 使用限制
rois为 FP32/FP16[R, 7](中心 xyz + 尺寸 xyz + 角度 rz);pts为同 dtype[P, 3];pts_feature为同 dtype[P, C]。R > 0、0 < P ≤ 1024、0 < C ≤ 1024、out_size[i] ∈ (0, 16]、max_pts_per_voxel ∈ [2, 256]、mode ∈ {0, 1}、三输入同 dtype 同设备。GetRequiredUbSize校验。max_pts_per_voxel - 1个点(首元素为计数,未用 slot 保持 0)。pooled_features/argmax为[R, outx, outy, outz, C],pts_idx_of_voxels为[R, outx, outy, outz, max_pts_per_voxel]。3. 方案设计
3.1 总体方案
3.1.1 RoiawarePool3d 算法概述
对第
b个框,中心(cx, cy, cz)、尺寸(sx, sy, sz)、绕 z 轴旋转角rz。点p = (x, y, z)变换到框局部坐标系:框内判定与体素编号:
池化(体素 v 内点集合 S):
体素索引表
pts_idx_of_voxels[..., 0]为计数,[..., 1..count]为点索引,超max_pts_per_voxel - 1截断。调用链(优化后):
flowchart LR A["mx_driving.roiaware_pool3d<br/>(Python)"] --> B["npu_roiaware_pool3d_forward<br/>(C++, 参数校验 + 输出分配)"] B --> C["rois → rois_coord + rois_angle<br/>pts → pts.transpose(0,1)"] C --> D["aclnnRoiawarePool3d"] D --> E["Host Tiling<br/>分核 + UB 校验"] E --> F["Ascend C Kernel<br/>向量化判定 + 池化"] F --> G["返回 (pooled_features, argmax, pts_idx_of_voxels)"]3.1.2 原始实现分析
master基线 Kernel 主流程与瓶颈:flowchart TD A["Process: 逐 box 循环"] --> B["collect_inside_pts_for_box3d<br/>逐点循环"] B --> C["DataCopy pts + roi<br/>(每点搬入, 含 PIPE_ALL)"] C --> D["Cos/Sin 逐点标量计算<br/>(每次 1 元素 + V→S 同步)"] D --> E["check_pt_in_box3d 标量判定"] E --> F["框内: 读体素表 → 计数+1 → 写回<br/>(每点一次 GM 往返)"] B --> G["compute_featrue<br/>逐体素 (outx·outy·outz)"] G --> H["channel 外层循环"] H --> I["逐 channel 标量 GetValue 比较<br/>Max/Add 仅 channel 0 执行一次"] style C fill:#ff6b6b,color:#fff style D fill:#ff6b6b,color:#fff style F fill:#ff6b6b,color:#fff style H fill:#ff6b6b,color:#fff style I fill:#ff6b6b,color:#fff瓶颈归纳:
ptsNum次 1 元素向量调用 + V→S 同步PIPE_ALL串行化DataCopy长度含未用容量Max/Add仅执行一次,其余 channel 退化标量SetBlockDim(coreNum)固定全核boxNum < coreNum时空转;无 UB 校验3.1.3 优化方案总览
优化后的 Kernel 将计算分为五个阶段,全部围绕"整点集向量化"组织:
flowchart TD S([Process 入口]) --> L["LoadPoints<br/>按维搬入全部点坐标 X/Y/Z<br/>(C++ 已转置为 [3,P])"] L --> LA["LoadAndComputeAngles<br/>搬入角度 → Muls(-1) → Cos/Sin 整行"] LA --> CI["CreateVecIndex<br/>生成点全局索引 0..P-1"] CI --> BLOOP{"逐 box 循环"} BLOOP --> LROI["LoadRoi<br/>搬 6 维 ROI + 预计算 halfSize/scale"] LROI --> CVI["ComputePointVoxelIds<br/>向量化局部坐标 + 框内判定 + 体素编号"] CVI --> GV["GatherValidPoints<br/>GatherMask 压紧 (voxelId, pointId)"] GV --> VLOOP{"逐 voxel 循环"} VLOOP -->|validCount==0| FAST["Duplicate 默认值直接写回"] VLOOP -->|非空| WIDX["写 pts_idx_of_voxels 计数与索引"] WIDX --> M{"mode"} M -->|0| MP["MaxPool<br/>整行 Max + Compare/Select argmax"] M -->|1| AP["AvgPool<br/>整行 Add + Muls(1/count)"] FAST --> VLOOP MP --> VLOOP AP --> VLOOP VLOOP -->|完成| BLOOP BLOOP -->|完成| E([结束])3.1.4 优化策略详解
优化 1:角度批量向量化(对应 P1)
新增独立输入
rois_angle(C++ 层从rois[:, 6]拆分),每个 Core 一次性搬入自己负责的所有 ROI 角度,Muls(-1)取负后Cos/Sin整行计算:LoadAndComputeAngles() { DataCopyPad(angleLocal_, angleGm_[roiStart_], ...); // 搬入 coreRoiNum 个角度 Muls(angleLocal_, angleLocal_, -1, coreRoiNum_); // 取负 Cos(cosLocal_, angleLocal_, coreRoiNum_); // 整行 cos(-angle) Sin(sinLocal_, angleLocal_, coreRoiNum_); // 整行 sin(-angle) SetFlag/WaitFlag<V_S>; // 一次同步供后续 Scalar 读取 }每个 box 处理时只需
cosLocal_.GetValue(localRoiIdx)一次标量读取,替代 master 版本每点一次的Cos/Sin+ 同步。优化 2:几何判定整点集向量化(对应 P2)
ComputePointVoxelIds对全部ptsNum个点并行执行:六个框内条件(x/y 的上下界、z 的上下界)通过
CompareScalar得到 mask 后用Select与validFlag累乘,一次遍历完成全部点的有效性标记,无任何逐点分支。优化 3:GatherMask 压紧,消除体素表逐点读改写(对应 P3)
GatherValidPoints:用CompareScalar(validFlag == 1)生成有效 mask,GatherMask一次压紧得到"有效点 voxelId 数组"与"有效点全局索引数组",复用已废弃的xIndexLocal_/yIndexLocal_buffer;GatherPointsAndPool:对每个体素用CompareScalar(voxelId == voxelIdx)生成 mask,GatherMask收集该体素内的点索引,一次性写计数与索引表:DataCopyPad(ptsIdxGm_[voxelOffset], countLocal_, countCopyParams_); // 计数(标量 S_MTE3) DataCopyPad(ptsIdxGm_[voxelOffset + 1], gatheredIndexLocal_, ...); // 索引(向量 V_MTE3)计数与索引分别以
S_MTE3/V_MTE3建立依赖,避免PIPE_ALL过度串行。优化 4:池化整体向量化(对应 P4)
Duplicate(poolFeature, -inf)初始化,循环内CopyPointFeature搬整行特征,Compare(pool ≥ tmp)+Select更新 argmax(GE 保留旧值 → first-point-wins),Max整行更新池化值,结束Cast(CAST_RINT)写回 argmax;Duplicate(0)初始化,循环内Add整行累加,结束后Muls(1/validCount)一次完成平均(倒数乘法替代 Div)。// MaxPool 核心(替代 channel 外层标量循环) for (pointOffset ...) { CopyPointFeature(pointIdx); // 搬整行 channelNum 特征 Compare(featureMask, poolFeature, tmpFeature, GE, channelNum_); Select(argmaxFloat, featureMask, argmaxFloat, pointIdx, ...); // first-point-wins Max(poolFeature, poolFeature, tmpFeature, channelNum_); // 整行 max } Cast(argmaxIntLocal_, argmaxFloatLocal_, CAST_RINT, channelNum_);优化 5:空体素快速路径(对应 P5)
if (validCount == 0) { Duplicate(poolFeatureLocal_, 0, channelNum_); Duplicate(argmaxIntLocal_, mode_ == 0 ? -1 : 0, channelNum_); DataCopyPad(pooledFeatureGm_[...], poolFeatureLocal_, ...); DataCopyPad(argmaxGm_[...], argmaxIntLocal_, ...); return; }优化 6:分核修正与 64 位 UB 校验(对应 P6)
usedCoreNum = min(coreNum, boxNum),SetBlockDim(usedCoreNum),coreBoxNums = boxNum / usedCoreNum、coreBoxTail = boxNum % usedCoreNum;GetRequiredUbSize(ptsNum, channelNum, maxCoreRoiNum),按 Kernel 实际 buffer 布局(点坐标 10×、掩码、点索引 6×、ROI/角度、特征 3×、特征掩码)以 64 位累加,ub_total_size == 0 || required > ub_total_size时拒绝任务。优化 7:GM 偏移 64 位化(对应 P7)
Kernel 中所有输出偏移(
voxelOffset、channelOffset、ptsFeatureGm_[pointIdx * channelNum])均以uint64_t计算,避免boxNum × voxelNum × channelNum在 32 位域溢出。优化 8:参数校验与输出分配下沉到 C++ 层(对应 P8)
npu_roiaware_pool3d_forward重构为"校验 + 分配 + 执行 + 返回"四段:rois [R,7]、pts [P,3]、pts_feature [P,C]、R>0、P∈(0,1024]、C∈(0,1024]、out_size∈(0,16]³、max_pts_per_voxel∈[2,256]、mode∈{0,1}、三输入同 dtype 同设备;rois_coord = rois[:, :6]、rois_angle = rois[:, 6](独立输入供批量向量化);pts_coord = pts.T(转置为[3, P]供按维搬运);argmax = at::empty(...)、pts_idx_of_voxels = at::zeros(...)(未用 slot 必须为 0 以保持公共语义)、pooled_features_cast = at::empty(...)(FP32 计算域);std::tuple(pooled_features, argmax, pts_idx_of_voxels),Python 层不再预分配。Python 层相应精简:
normalize_out_size统一int/三元组;删除内联校验与输出分配;ctx直接保存反向所需上下文。3.1.5 优化前后对比
validCount==0快速路径SetBlockDim(coreNum)(可能空转)min(coreNum, boxNum)GetRequiredUbSize3.2 技术选型
选择理由:优化针对的是 master 中最确定的冗余——重复角度计算、逐点标量判定与逐 channel 标量池化。整点集向量化让 Vector 单元承担几何判定主负载,GatherMask 压紧把体素索引写从"每点读改写"降为"每体素一次写"。新增
rois_angle输入与pts转置的成本由 C++ 层一次性承担,换来 Kernel 侧按维搬运与整行角度计算的简化。按 box 分核保持既有并行模型,避免跨核合并复杂度。3.3 功能与性能设计
3.3.1 Tiling 设计
多核划分:
usedCoreNum = min(coreNum, boxNum),按 box 连续分配:Core
c的 ROI 起始:TilingData(
RoiawarePool3dTilingData,与 master 字段一致):BEGIN_TILING_DATA_DEF(RoiawarePool3dTilingData) TILING_DATA_FIELD_DEF(uint32_t, coreBoxNums); TILING_DATA_FIELD_DEF(uint32_t, coreBoxTail); TILING_DATA_FIELD_DEF(uint32_t, boxNum); TILING_DATA_FIELD_DEF(uint32_t, ptsNum); TILING_DATA_FIELD_DEF(uint32_t, channelNum); TILING_DATA_FIELD_DEF(uint32_t, maxPtsPerVoxel); TILING_DATA_FIELD_DEF(uint32_t, outx); TILING_DATA_FIELD_DEF(uint32_t, outy); TILING_DATA_FIELD_DEF(uint32_t, outz); TILING_DATA_FIELD_DEF(uint32_t, coreNum); TILING_DATA_FIELD_DEF(uint32_t, mode); END_TILING_DATA_DEF;UB 布局(
GetRequiredUbSize64 位累加,与 KernelInitBuffer一一对应):pointX/Y/Z、shiftX/Y、localX/Y/Z、tmpPts、validFlag10 × alignedPtsNum × 4pointMaskAlignUp((ptsNum+7)/8, 32)xIndex/yIndex/zIndex/voxelId/pointIndex/gatheredIndex6 × alignedPtsNum × 4roi、angle、cos、sin(8 + 3 × alignedCoreRoiNum) × 4count、argmaxInt(8 + alignedChannelNum) × 4poolFeature、tmpFeature、argmaxFloat3 × alignedChannelNum × 4featureMaskAlignUp((channelNum+7)/8, 32)其中
alignedPtsNum = AlignUp(ptsNum, 8)、alignedChannelNum = AlignUp(channelNum, 8)、alignedCoreRoiNum = AlignUp(maxCoreBoxNums, 8)。输出 shape(InferShape):
3.3.2 核心流程
优化后的
Process()主流程:flowchart TD START([Process 入口]) --> INIT["Init<br/>Tiling 参数 + GM/UB 绑定<br/>按 box 计算 roiStart"] INIT --> LP["LoadPoints<br/>按维搬入全部点坐标 X/Y/Z"] LP --> LA["LoadAndComputeAngles<br/>角度取负 + 整行 Cos/Sin"] LA --> CI["CreateVecIndex<br/>生成点全局索引 0..P-1"] CI --> BLOOP{"逐 box 循环"} BLOOP --> LROI["LoadRoi<br/>搬 6 维 ROI + 预计算 halfSize/scale"] LROI --> CVI["ComputePointVoxelIds<br/>向量化局部坐标 + 框内判定 + 体素编号"] CVI --> GV["GatherValidPoints<br/>GatherMask 压紧 (voxelId, pointId)"] GV --> VLOOP{"逐 voxel 循环"} VLOOP -->|validCount==0| FAST["Duplicate 默认值<br/>pool=0, argmax=-1/0"] VLOOP -->|非空| WIDX["写 pts_idx_of_voxels 计数与索引"] WIDX --> M{"mode"} M -->|0| MP["MaxPool<br/>整行 Max + Compare/Select argmax"] M -->|1| AP["AvgPool<br/>整行 Add + Muls(1/count)"] FAST --> VLOOP MP --> VLOOP AP --> VLOOP VLOOP -->|完成| BLOOP BLOOP -->|完成| DONE([结束])池化内层(每体素):
flowchart LR A["CopyPointFeature<br/>DataCopy 整行 channelNum 特征"] --> B{"mode"} B -->|0| C["Compare(pool ≥ tmp) GE<br/>Select(argmax, 保留旧值 first-wins)"] C --> D["Max(pool, pool, tmp, channelNum)"] B -->|1| E["Add(pool, pool, tmp, channelNum)"] D --> F["循环完: Cast argmax → INT32"] E --> F F --> G["DataCopyPad 写 pooled_features / argmax"]同步设计:点坐标搬入后
MTE2_V;角度向量化后V_S;ROI 搬入后MTE2_S;体素索引写计数用S_MTE3、写索引用V_MTE3;特征搬移用V_MTE2/MTE2_V;体素间复用 Vector/标量结果时仅等待对应MTE3_V/MTE3_S,避免PIPE_ALL过度串行。3.3.3 性能提升分析
理论收益(以每 box
ptsNum个点、voxelNum个体素、channelNum个通道计):ptsNum × 2次 1 元素调用/box(ptsNum-1) × 2次向量调用与 V→S 同步PIPE_ALLvoxelNum次/box(channelNum-1)轮标量validCount次整行向量min(coreNum, boxNum)预计收益来源:角度计算与体素索引写是纯消除项;几何判定与池化的向量化将大量标量读写替换为整行 Vector 指令,对
channelNum ∈ [64, 512]、outx·outy·outz较大且体素稀疏的典型配置收益最大。3.4 安全隐私与 DFX 设计
3.4.1 安全性增强
context/ 输入 Tensor /platformInfoPtr/attrs为空coreNum == 0/usedCoreNum == 0ub_total_size == 0 || GetRequiredUbSize > ub_total_sizeGetBlockNum() == 0out_size/max_pts_per_voxel/mode范围3.4.2 兼容性设计
mx_driving.roiaware_pool3d(rois, pts, pts_feature, out_size, max_pts_per_voxel, mode)不变,返回值语义不变。Compare(GE)+Select保留旧值保持;空体素默认值(max=-1、avg=0)与 master 一致;pts_idx_of_voxels未用 slot 保持 0(C++ 层at::zeros分配)。roisAngle输入、pts布局改为转置[3, P],Host OpDef、ACLNN 生成物、Kernel 签名与 C++ 调用必须来自同一构建版本。roiaware_pool3d_grad依赖的pts_idx_of_voxels/argmax布局不变。3.4.3 可测试性设计
tests/torch/test_roiaware_pool3d.py,参数化用例固定种子20260702。RANDOM_CASES覆盖:max/avg × FP32/FP16 × 通道数 64/256/512 × 体素网格 (4,4,4)/(2,3,4)/(1,1,1) × 点数 96~256。max_pts_per_voxel=3保留前 2 点)、空体素(点全在框外)。assertRtolEqual对比。TORCH_CHECK显式报错,UT 覆盖非法参数路径。3.4.4 可靠性设计
CopyPointFeature使用V_MTE2/MTE2_V建立"索引(Vector)→ 特征搬移(MTE2)→ 向量计算"的依赖,避免PIPE_ALL过度串行。MTE3_V/MTE3_S依赖。S_MTE3/V_MTE3精确同步,防止标量计数与向量索引乱序。Cast(CAST_RINT)转 INT32,避免直接浮点→整型截断误差。uint64_t计算,防止溢出。3.5 编程与调用设计
3.5.1 编程模型基本设计
开发环境:
kernel_operator.h、kernel_tiling/kernel_tiling.h、tiling/tiling_api.h开发约束:
rois/pts为 FP32/FP16,pts_feature同 dtype;P ≤ 1024、C ≤ 1024、out_size ≤ 16、max_pts_per_voxel ∈ [2, 256];UB 受GetRequiredUbSize校验。可验收设计:
assertRtolEqual通过;argmax/索引语义一致RANDOM_CASES+ 定向用例通过ops_performance(待补录)mx_driving.roiaware_pool3d全链路3.5.2 接口定义与设计
3.5.2.1 RoiawarePool3d Kernel(roi 分支)
extern "C" __global__ __aicore__ void roiaware_pool3d( GM_ADDR rois, GM_ADDR roisAngle, GM_ADDR pts, GM_ADDR ptsFeature, GM_ADDR argmax, GM_ADDR ptsIdxOfVoxels, GM_ADDR pooledFeatures, GM_ADDR workspace, GM_ADDR tiling);与 master 的差异:新增
roisAngle(角度独立输入,支持批量向量化);pts布局为 C++ 转置后的[3, P]。参数:
rois[R, 6]roisAngle[R]pts[3, P]ptsFeature[P, C]argmax[R, ox, oy, oz, C]ptsIdxOfVoxels[R, ox, oy, oz, maxPtsPerVoxel]pooledFeaturesworkspaceSetSysWorkspacetilingRoiawarePool3dTilingData3.5.2.2 C++ 封装(
npu_roiaware_pool3d_forward)std::tuple<at::Tensor, at::Tensor, at::Tensor> npu_roiaware_pool3d_forward( const at::Tensor &rois, const at::Tensor &pts, const at::Tensor &pts_feature, at::IntArrayRef out_size, int32_t max_pts_per_voxel, int32_t mode);rois拆分为rois_coord(前 6 列)与rois_angle(第 6 列),pts转置为[3, P];argmax = at::empty、pts_idx_of_voxels = at::zeros(未用 slot 保持 0)、pooled_features = at::empty(FP32 域);3.5.2.3 Python 封装(
mx_driving/ops/roiaware_pool3d.py)normalize_out_size(out_size):int/三元组统一为三元组;_C.npu_roiaware_pool3d_forward返回三元组,ctx保存反向上下文;_C.roiaware_pool3d_grad计算grad_in,返回与 forward 参数一一对应的梯度元组。import torch import mx_driving rois = torch.randn(10, 7, dtype=torch.float32).npu() pts = torch.randn(128, 3, dtype=torch.float32).npu() pts_feature = torch.randn(128, 256, dtype=torch.float32).npu() # mode=0 为 max 池化,返回 pooled_features [10, 4, 4, 4, 256] out = mx_driving.roiaware_pool3d(rois, pts, pts_feature, out_size=4, max_pts_per_voxel=128, mode=0)3.5.3 编程手册设计
公共 API 无变化,无需更新用户侧调用方式。内部开发者参考:
kernels/roiaware_pool3d/op_kernel/roiaware_pool3d.cpp——加载 / 角度 / 几何判定 / 压紧 / 池化五段式方法结构。kernels/roiaware_pool3d/op_host/roiaware_pool3d.cpp、roiaware_pool3d_tiling.h——按 box 分核(min(coreNum, boxNum))与 64 位 UB 容量校验。mx_driving/csrc/RoiawarePool3d.cpp——参数校验、输入整理(rois 拆分、pts 转置)、输出分配。tests/torch/test_roiaware_pool3d.py——参数化用例与固定种子。4. 测试设计
4.1 单元测试(UT)
复用
tests/torch/test_roiaware_pool3d.py(种子20260702):精度验证:模块级
roiaware_pool3d_golden(numpy 实现局部坐标变换、框内判定、体素编号、max/avg 池化),与 NPU 输出assertRtolEqual对比;FP16 用例在 FP32 域计算 golden 后转回 FP16。性能验证(待补录):同环境 baseline(master 分支)vs candidate(roi 分支),预热 ≥ 50 次、采样 ≥ 100 次、
torch.npu.synchronize()后统计 Device Time,矩阵覆盖多框(R ∈ [1, 1024])、多通道(64~512)、稠密/稀疏体素、FP32/FP16。4.2 集成测试
mx_driving.roiaware_pool3d验证完整链路:Python →_C.npu_roiaware_pool3d_forward→aclnnRoiawarePool3d→ Host Tiling → Kernel → 输出返回。roiaware_pool3d_grad,确认pts_idx_of_voxels/argmax布局未被破坏。4.3 端到端测试
附录
关键代码位置
kernels/roiaware_pool3d/op_kernel/roiaware_pool3d.cppkernels/roiaware_pool3d/op_host/roiaware_pool3d.cppkernels/roiaware_pool3d/op_host/roiaware_pool3d_tiling.hmx_driving/csrc/RoiawarePool3d.cppmx_driving/ops/roiaware_pool3d.pytests/torch/test_roiaware_pool3d.py