Open
test #2
zl_hwAdd on  Apr 20
zl_hwCollaborators
Apr 20 Create

TTK 三腿比对与 golden 编写规范

适用于所有接入 TTK 的算子。文中标「源码」的是在 TTK 仓核实过的实现事实。


一、三条腿的角色

腿 谁来算 计算精度 角色
NPU kernel 用输入类型计算;内部是否加宽、宽到什么类型,由实现自己决定 被测对象,也是整套规则的基准
CPU golden 见第三节 三方模式下的高精度真值
GPU 三方腿(竞品实现) 用输入类型;按 NPU 的加宽行为同步 cast,出口对应地窄回 同精度标尺

只有三方比对才有「真值」一说。 两方泛化是 NPU 与 CPU 两个平级实现互比,谁都不是真值。


二、走几方:由判据决定

「源码」ttk/core_modules/npu/op/profiling.py:

standards   = _resolve_tolerance(...)      # 逐输出解析,用的是**未提升前**的 dtype
need_3party = any(s.token == "cross_check" for s in standards)
if need_3party:
    context.golden_mode_override = "Promote"
三方比对 两方泛化
触发 判据含 cross_check 其余判据(binary_equal / close / mix_tolerance …)
TTK 是否 Promote 开 不开
是否执行 GPU 腿 执行 不执行
有无「真值」 有 无,平级互比

「源码」三方腿门控只看 need_3party,没有任何 dtype 过滤
(_run_xpu_and_workspace(context, need_3party) → _xpu_mode(switches, need_3party))。
need_3party 是用例级的:多输出算子只要有一个输出声明 cross_check,整个用例就走三方。


三、CPU(golden)要不要 cast —— 核心规则

                     该张量是浮点还是整型 ?
                              │
              ┌───────────────┴───────────────┐
            浮点                             整型
              │                               │
      判据含 cross_check ?          整型**没有三方一说**
              │                     (TTK 不提升整型,也不跑 GPU 腿)
       ┌──────┴──────┐                        │
      是            否                         │
       │             │                         │
  ┌────▼─────┐  ┌────▼──────────────────┐ ┌───▼──────────────────┐
  │ 零 cast  │  │ 按 NPU 的加宽行为      │ │ 按 NPU 的加宽行为     │
  │ 入口出口 │  │ 决定是否 cast 放宽     │ │ 决定是否 cast 放宽    │
  │ 都不做   │  │                       │ │                      │
  │ TTK 的   │  │ NPU 不宽 → 也不宽      │ │ NPU 不宽 → 也不宽     │
  │ Promote  │  │ NPU 宽到哪 → 就宽到哪  │ │ NPU 宽到哪 → 就宽到哪 │
  │ 负责     │  │ 出口跟随其窄回行为     │ │ 出口跟随其窄回行为    │
  └──────────┘  └───────────────────────┘ └──────────────────────┘

一句话:只有「浮点 + 三方」这一格是零 cast,其余全部「跟 NPU」。

「源码」DTYPE_PROMOTE_MAP(ttk/utilities/dtypes.py)只含浮点与复数:

fp16 → fp32      bf16 → fp32      fp32 → fp64
complex32 → complex64             complex64 → complex128
fp8(各变体) → fp32

无任何整型条目 —— 这就是「整型没有三方」的根源:既不提升,也组不成双标杆。

「源码」Promote 时 TTK 同时抬高 input_arrays、input_dtypes 和 output_dtypes,
golden 算完在 finally 里全部还原(output_generation.py: __promote_dtype)。
注意:golden 收到的 output_dtypes 是提升后的值,据它取 dtype 不等于取到算子声明的 dtype。


四、完整逻辑图

                         算子声明 dtype
                               │
                    判据解析 (未提升前的 dtype)
                               │
                 need_3party = 判据含 cross_check ?
                               │
        ┌──────────────────────┴──────────────────────┐
       是                                            否
        │                                             │
  ═══ 三方比对 ═══                              ═══ 两方泛化 ═══
  TTK 开 Promote                                TTK 不提升
  执行 GPU 腿                                    不执行 GPU 腿
  有"真值"                                       无"真值", 平级互比
        │                                             │
┌───────┼────────┬──────────┐              ┌──────────┼──────────┐
│       │        │          │              │          │          │
▼       ▼        ▼          ▼              ▼          ▼          ▼
NPU   CPU      GPU      判据             NPU        CPU       判据
│      │        │      cross_check        │          │      binary_equal
│      │        │      比值判定           │          │      /close 等
│      │        │                         │          │      逐位或容差
│      │        │                         │          │
│      │      按 NPU                      │     **按 NPU 的加宽行为**
│      │      加宽行为                     │       决定是否 cast 放宽
│      │      同步 cast                    │       (NPU 不宽则不宽,
│      │      出口对应窄回                  │        NPU 宽到哪就宽到哪,
│      │                                   │        出口跟随其窄回行为)
│      │                                   │
│   ┌──┴── 该张量是浮点还是整型 ? ──┐       │
│   │                              │       │
│ 浮点                            整型      │
│   │                              │       │
│ 零 cast                   **按 NPU 的加宽行为**
│ TTK 抬一档                  决定是否 cast 放宽
│ (fp16/bf16→fp32            (TTK 不提升整型;
│  fp32→fp64)                 出口跟随 NPU 窄回)
│ 出口也不 cast
│                                          │
└─ 用输入类型算, 内部是否加宽由实现决定 ────┘
   输出不 cast                              ← 两支里 NPU 行为相同, 它就是基准

五、四格对照表

场景 NPU CPU(golden) GPU(三方腿) 判据
浮点 + 三方 输入类型算,实现自定加宽,输出不 cast 完全不 cast 按 NPU 加宽行为同步 cast,出口对应窄回 cross_check(比值)
浮点 + 两方泛化 同上 按 NPU 加宽行为 cast 不参与 close 等(容差)
整型 + 三方
(多输出混合 dtype 时才出现)
同上 按 NPU 加宽行为 cast 整型张量会进 GPU 腿,同样按 NPU 加宽行为 该输出自己的判据
整型 + 两方 同上 按 NPU 加宽行为 cast 不参与 binary_equal(逐位)

六、各腿用什么框架实现

腿 框架 强度
GPU(三方腿) torch 固定 —— 要借它拿性能数据
CPU(golden) 该算子含 tf 通路 → 用 tf 实现
不含 tf 通路 → 推荐 torch
前者是要求,后者是推荐
numpy 只作输入输出的辅助(reshape / indices 转换 / 出口窄回等) 不推荐用作计算逻辑(逻辑上允许,但不提倡)

判断该算子是否含 tf 通路,看 golden 的注册:

__golden__ = {
    "kernel": {"<op_name>": "..."},
    "e2e":    {"tf.compat.v1.<op>": "..."},   # ← 有这一项即含 tf 通路
}

七、判据声明

_TOL_KERNEL / _TOL_E2E 必须覆盖算子 def 注册的全部 dtype。
缺声明的 dtype 会落到 TTK 的默认判据(mix_tolerance)上,而不是算子自己声明的标准 —— 属规范缺口。

对照方法:拿 op_host/<op>_def.cpp 里的 DT_* 集合与 _TOL_KERNEL 的键逐一比对。


八、常见误区(都是实际踩过的)

误区 实情
golden 里 astype(np.float32) 把结果统一成算子 dtype 撤销 Promote。真值塌到与三方腿同精度 → |三方−golden| 趋零 → 三比值分母被夹 → RMSE 比值爆表假红
出口按「算子声明的 dtype」取 Promote 下 output_dtypes 已被抬高,按声明 dtype 取会砍回去;按下发的 var.dtype 取才是对的
整型抬到 int64「防溢出」 改变溢出语义。内核在 AccT 宽度上回绕,golden 留在 int64 会规避掉(如 INT_MIN / -1),两边结果不一致
模 2ⁿ 回绕下「每步窄回」与「最后窄回」等价 对 + − × 成立(环同态),对整数除法不成立。所以只有 div 类算子会因抬位而偏离
用 --compare close 做「不跑三方的泛化」 close 覆盖声明判据 → need_3party=False → 连带关掉 Promote,双标杆塌成单标杆,浮点必然假红
以为 TTK 会替 golden 桥接 bf16 「源码」只有 numpy-API 路径(__call_numpy_api)会桥接。自定义 golden 走 __call_custom_golden_func,不桥接。而 torch/tf 本就原生支持 bf16,按推荐框架写就没这问题
三方腿出口忘了窄回 三方停在加宽后的类型,与走 Promote 的 golden 逼近逐位相等 → 双标杆塌成单标杆
三方腿只窄浮点、不窄整型 torch 在整型路径会把中间量升到 int32,int8/uint8 出参会漏成 int32
torch 整型除法可直接用 INT_MIN / -1 触发 SIGFPE 硬件陷阱(进程直接崩)。回绕语义下 x / -1 ≡ -x,把除数为 -1 的位置改走 neg 即可规避
tf 整型除法同理 tf 的 truncatediv(INT_MIN, -1) 正常回绕不崩;但 truncatediv(x, 0) 会抛 InternalError,除零仍须自行守护

附录:查证方法

要查什么 怎么查
该 dtype 会不会被 Promote ttk/utilities/dtypes.py 的 DTYPE_PROMOTE_MAP
本用例走不走三方 判据里有没有 cross_check(_resolve_tolerance 的结果)
golden 走哪条 dispatch framework_of(func) 读函数真实 __module__:numpy/torch/tf → 对应路径;自定义函数 → __call_custom_golden_func
NPU 的加宽行为 看 kernel 的累加类型定义(如 AccT)与 LoadWiden / NarrowStore 一类的搬运封装
算子支持面 op_host/<op>_def.cpp 的 DT_*
likedislike
Zzl_hwCollaborators
Apr 21 Modify description of issue
Zzl_hwCollaborators
Apr 22 Modify description of issue
Zzl_hwCollaborators
Apr 22 Modify description of issue
Zzl_hwCollaborators
Apr 29 Modify description of issue
Zzl_hwCollaborators
Apr 29 Modify description of issue
Zzl_hwCollaborators
Apr 29 Modify description of issue
Zzl_hwCollaborators
Apr 29 Modify description of issue
Zzl_hwCollaborators
Jun 5 Modify description of issue
TangPCTangPCCollaborators
Sep 14 Modify description of issue
Rraoliang_sacCollaborators
29 days ago Associated pull request
Rraoliang_sacCollaborators
29 days ago Associated pull request
Rraoliang_sacCollaborators
21 days ago Associated pull request
Rraoliang_sacCollaborators
21 days ago Associated pull request