已关闭
[Requirement|需求建议]: TopkV2算子在小k情况下支持双调排序的实现 #5236
cy_hw创建于  8月27日关闭于  9月1日
cy_hw
cy_hw成员
8月27日 创建

aclnnTopkV2 接口支持TopKV2算子实现双调排序设计

1. 概述

TopK 算子用于沿指定轴取出输入张量的前 K 个极值及其下标。aclnnTopkV2 是对原有 aclnnTopk(下称 V1)接口的扩展版本,核心差异是新增 int64_t sortPolicy 参数,将排序策略选择权上交给调用方,从而支持 Bitonic Sort 等新计算路径。

V2 与 V1 共享同一套内部实现(aclnnTopkGetWorkspaceSizeCommon),二者均遵循 CANN 标准两段式(GetWorkspaceSize + Run)调用模型。

2. 接口签名

// 第一段:计算 workspace 并构建 executor
aclnnStatus aclnnTopkV2GetWorkspaceSize(
    const aclTensor* self, int64_t k, int64_t dim, bool largest,
    bool sorted, int64_t sortPolicy,            // ← 相比 V1 新增
    aclTensor* valuesOut, aclTensor* indicesOut,
    uint64_t* workspaceSize, aclOpExecutor** executor);

// 第二段:在 stream 上执行计算
aclnnStatus aclnnTopkV2(void* workspace, uint64_t workspaceSize,
                        aclOpExecutor* executor, const aclrtStream stream);

3. 功能说明

3.1 aclnnTopkV2GetWorkspaceSize(第一段接口)

见 aclnn_topk.cpp:893。负责"编译期"工作:

  1. DFX 打点:通过 L2_DFX_PHASE_1 记录输入(含 sortPolicy)与输出张量,用于性能与问题追溯。
  2. 委托公共实现:直接调用 aclnnTopkGetWorkspaceSizeCommon(aclnn_topk.cpp:672),将 sortPolicy 透传下去,由公共函数完成全部参数校验、策略选择与图构建。
  3. 产出:返回 workspaceSize(所需 workspace 大小)与 executor(已构建的计算执行器)。

公共函数 aclnnTopkGetWorkspaceSizeCommon 完成的关键步骤:

  • 参数校验:非空、format、k/dim 合法性、dtype 合法性、维数上限(CheckParams,aclnn_topk.cpp:184)。
  • 空 tensor 与 0 维 tensor 的适配(TopkAdaptInputZeroDimTensor,aclnn_topk.cpp:204)。
  • 输入连续化(l0op::Contiguous)与 910 平台 fp32→fp16 的 GE cast 适配(TopkAdaptGeCastTensor,aclnn_topk.cpp:229)。
  • 多策略分支选择(见第 5 节)。
  • 输出 shape 校验、结果类型 cast 回原 dtype、ViewCopy 写回用户输出。
  • *workspaceSize = executor->GetWorkspaceSize()。

3.2 aclnnTopkV2(第二段接口)

见 aclnn_topk.cpp:902。负责"运行期"工作:

  1. DFX 打点:L2_DFX_PHASE_2(aclnnTopkV2) 标记第二段进入。
  2. 执行计算:调用框架通用能力 CommonOpExecutorRun(workspace, workspaceSize, executor, stream),将第一段构建的 executor 在指定 aclrtStream 上异步执行。

实现与 aclnnTopk 完全一致,仅 DFX 标签不同(用于区分 V1/V2 的运行统计)。

4. 两段式调用关系

调用方
  │
  ├─① aclnnTopkV2GetWorkspaceSize(self,k,dim,largest,sorted,sortPolicy,...,&ws,&executor)
  │        │
  │        └─► aclnnTopkGetWorkspaceSizeCommon(... sortPolicy ...)
  │                │  参数校验 / 连续化 / 策略选择 / 图构建
  │                └─► workspaceSize、executor 返回给调用方
  │
  │   (调用方申请 workspace 内存)
  │
  └─② aclnnTopkV2(workspace, workspaceSize, executor, stream)
           │
           └─► CommonOpExecutorRun(...)  // 在 stream 上异步执行

第一段不执行真正的数值计算,只做规划与 executor 构建;第二段才在 NPU stream 上执行。这种拆分允许框架在 host 侧完成 Tiling/图融合后再下发设备执行。

5. 与 aclnnTopk(V1)的关系与差异

维度 aclnnTopk(V1) aclnnTopkV2
第一段签名参数 无 sortPolicy 新增 int64_t sortPolicy
第一段实现 调用 Common,硬编码 sortPolicy=0(aclnn_topk.cpp:882) 调用 Common,透传调用方传入的 sortPolicy(aclnn_topk.cpp:898)
第二段实现 CommonOpExecutorRun CommonOpExecutorRun(仅 DFX 标签不同)
策略可控性 策略由内部阈值自动决定,不可外部干预 允许外部指定排序策略(如 Bitonic)
兼容性 — V1 等价于 aclnnTopkV2(..., sortPolicy=0),是 V2 的子集

结论:V2 是 V1 的超集/扩展。V1 = V2(sortPolicy=0)。两者复用同一公共实现,差异仅在于是否向调用方暴露 sortPolicy。

6. sortPolicy 参数与策略选择

sortPolicy 在公共实现中影响两处:

6.1 Bitonic Sort 路径判定

IsBitonicSort(aclnn_topk.cpp:666):

return k >= BITONIC_SORT_MIN_K_THRESTHOD     // k >= 2
    && k <= BITONIC_SORT_MAX_K_THRESTHOD     // k <= 32
    && sorted                                // 要求排序
    && sortPolicy == TOP_K_BITONIC_SORT_POLICY; // sortPolicy == 1

当满足"小 K(2~32)+ 需排序 + sortPolicy=1"时,跳过 Sort/SortAndTopK/RadixTopK 分支,进入默认 l0op::Topk 路径,由底层算子按 Bitonic 排序策略执行。该路径针对小 K 排序场景做了优化。

6.2 透传至底层 Topk

在 NoTranspose 分支与默认 Topk 分支中,sortPolicy 作为参数传给 l0op::Topk(aclnn_topk.cpp:747、aclnn_topk.cpp:785、aclnn_topk.cpp:838),由底层算子决定具体排序实现。V1 因硬编码为 0,底层走默认策略。

7. 公共实现的整体策略分支

aclnnTopkGetWorkspaceSizeCommon 按以下优先级选择计算路径(顺序即代码判定顺序):

  1. TopkAxisOneCopy(aclnn_topk.cpp:718):950 上 K=1 且排序轴=1,直接拷贝 + 索引填 0。
  2. TopKCopy(aclnn_topk.cpp:732):排序轴==K 且不排序(950),整段拷贝 + 生成连续索引。
  3. NoTranspose(aclnn_topk.cpp:743):950 且非尾轴、轴长在阈值内、收益可期,直接对非尾轴做 Topk,避免转置开销。
  4. 非尾轴通用分支(positiveDim != lastDim,先转置到尾轴再计算,再转置回去),其内子策略:
    • Sort:整轴排序(IsSort,K==轴长且可处理)。
    • SortAndTopK:先全排序再切前 K(IsSortAndTopK,按 dtype/轴长/k 占比阈值)。
    • RadixTopK:仅 910B/910_93、fp16/bf16、大 K 场景(IsRadixTopKSupported)。
    • 默认 l0op::Topk(透传 sortPolicy)。
  5. 尾轴分支,子策略同上,外加 910 小 K 非 950 的 并行两级 Topk(aclnn_topk.cpp:797,先取 PARALLEL_K=32 个候选再取最终 K)。

IsBitonicSort 在第 4、5 分支中作为 Sort/SortAndTopK/RadixTopK 的前置短路条件:命中时直接走默认 Topk,避免这些策略覆盖小 K 排序场景。

8. 关键常量速查

常量 值 含义
BITONIC_SORT_MIN_K_THRESTHOD 2 Bitonic 路径 K 下限
BITONIC_SORT_MAX_K_THRESTHOD 32 Bitonic 路径 K 上限
TOP_K_BITONIC_SORT_POLICY 1 触发 Bitonic 的 sortPolicy 值
PARALLEL_K 32 910 小 K 两级 Topk 的候选数
SORT_WITH_INDEX_THRESHOLD 2000 是否需对 indices 做 int32→int64 cast 的阈值
SORT_AND_TOP_K_THRESHOLD 0.5 k/轴长 ≥ 该比值时走 SortAndTopK
RADIX_TOP_K_MIN_K 1000 Radix TopK 的 K 下限

9. 设计小结

  • 复用优先:V2 不重写逻辑,通过共享 aclnnTopkGetWorkspaceSizeCommon 与 V1 复用全部校验与策略代码,仅扩展入参。
  • 最小侵入扩展:新增 sortPolicy 是透传式参数,对已有路径零影响(V1 行为等价 sortPolicy=0)。
  • 策略外化:把原本仅由内部阈值决定的排序策略部分交给调用方,便于上层(如框架图引擎)针对小 K 排序场景显式选择 Bitonic 路径以获取更优性能。
  • 两段式不变:保持 CANN 标准 GetWorkspaceSize + Run 模型,第二段实现与 V1 完全相同,便于复用运行时基础设施。
likedislike
cy_hwcy_hw成员
8月27日 添加了label:requirement
cy_hwcy_hw成员
8月27日 关联了pull request:新增aclnnTopkV2接口适配TopkV2算子支持小k场景下的双调排序
yuning_chenyuning_chen成员
8月27日 将 caoyan_huawei 设为负责人
cy_hwcy_hw成员
8月29日 修改了issue 的描述
cy_hwcy_hw成员
8月29日 修改了issue 的描述
CANN-robotCANN-robot成员
9月1日 关闭了 issue
CANN-robotCANN-robot成员
9月1日 添加了label:resolved