diff --git a/agents/ascend-kernel-developer.md b/agents/ascend-kernel-developer.md
index f527fbea..cc76c14a 100644
--- a/agents/ascend-kernel-developer.md
+++ b/agents/ascend-kernel-developer.md
@@ -50,6 +50,39 @@ Phase 6: 全量用例验证
Phase 7: Trace 记录 (trace-recorder)
```
+## Hook 机制说明
+
+本项目的 `.claude/settings.json` 已配置 **toolUse hook**,用于拦截 agent 对 skill 相关脚本的 Bash 调用。
+
+### 被拦截的脚本
+
+| 类别 | 脚本 | 说明 |
+|------|------|------|
+| 退化检测 | `validate_tilelang_impl.py` | TileLang AST 退化检测 |
+| 退化检测 | `validate_ascendc_impl.py` | AscendC AST 退化检测 |
+| 评测脚本 | `evaluate_tilelang.sh` | TileLang 功能验证 |
+| 评测脚本 | `evaluate_ascendc.sh` | AscendC 功能验证 |
+| 构建脚本 | `utils/build_ascendc.py` | AscendC kernel 编译 |
+| 验证脚本 | `utils/verification_ascendc.py` | AscendC 正确性验证 |
+| 验证脚本 | `utils/verification_tilelang.py` | TileLang 正确性验证 |
+| 性能测试 | `performance.py` | 性能对比测试 |
+| 批处理 | `batch_run_performance.sh` | 批量性能测试 |
+
+### Hook 行为
+
+1. **拦截**: 当 agent 通过 Bash tool 调用上述脚本时,hook 自动拦截
+2. **替换执行**: 由 `.claude/hooks/skill_script_hook.py` 接管执行
+3. **等待完成**: hook 等待脚本实际执行完毕(同步阻塞)
+4. **返回结果**: 将 exit code、stdout、stderr 以 JSON 格式返回给 agent
+5. **Agent 继续**: agent 收到结果后才继续下一步
+
+### 配置位置
+
+- Hook 脚本: `.claude/hooks/skill_script_hook.py`
+- Hook 配置: `.claude/settings.json`
+
+> **注意**: 非拦截命令(如普通 `ls`、`cp`、`python` 调用其他脚本)会透传执行,不受影响。
+
### 退化检测脚本
| 阶段 | 脚本路径 | 说明 |
diff --git a/benchmarks/NPUKernelBench/level1/11_GroupNorm.py b/benchmarks/NPUKernelBench/level1/11_GroupNorm.py
index 01295f11..3d44bb79 100644
--- a/benchmarks/NPUKernelBench/level1/11_GroupNorm.py
+++ b/benchmarks/NPUKernelBench/level1/11_GroupNorm.py
@@ -2,6 +2,7 @@
import torch.nn as nn
import json
import os
+import random
class Model(nn.Module):
"""
@@ -34,13 +35,13 @@ def get_input_groups():
input_groups = []
for idx, case in enumerate(cases):
inputs = case["inputs"]
-
+
dtype_map = {
"float32": torch.float32,
"float16": torch.float16,
"bfloat16": torch.bfloat16,
}
-
+
x_info = inputs[0]
dtype = dtype_map[x_info["dtype"]]
if idx % 2 == 0:
@@ -53,7 +54,7 @@ def get_input_groups():
num_groups = None
weight = None
bias = None
-
+
for inp in inputs[1:]:
if inp["name"] == "num_groups":
num_groups = inp["value"]
@@ -61,7 +62,7 @@ def get_input_groups():
weight = torch.randn(inp["shape"], dtype=dtype)
elif inp["name"] == "bias":
bias = torch.randn(inp["shape"], dtype=dtype)
-
+
input_groups.append([x, num_groups, weight, bias])
return input_groups
diff --git a/benchmarks/NPUKernelBench/level1/25_NLLLoss.py b/benchmarks/NPUKernelBench/level1/25_NLLLoss.py
index 841bbba2..538e2aa0 100644
--- a/benchmarks/NPUKernelBench/level1/25_NLLLoss.py
+++ b/benchmarks/NPUKernelBench/level1/25_NLLLoss.py
@@ -33,13 +33,13 @@ def get_input_groups():
json_path = os.path.join(os.path.dirname(__file__), "25_NLLLoss.json")
with open(json_path, "r") as f:
cases = [json.loads(line) for line in f if line.strip()]
-
+
input_groups = []
for idx, case in enumerate(cases):
inputs = case["inputs"]
input_info = inputs[0]
target_info = inputs[1]
-
+
dtype_map = {
"float32": torch.float32,
"float16": torch.float16,
@@ -56,11 +56,11 @@ def get_input_groups():
target_range = target_info.get("range", [0, input_info["shape"][1] - 1])
target = torch.randint(target_range[0], target_range[1] + 1, tuple(target_info["shape"]), dtype=torch.int64)
-
+
weight = None
ignore_index = -100
reduction = "mean"
-
+
for inp in inputs[2:]:
if inp["name"] == "weight":
weight = torch.randn(inp["shape"], dtype=dtype)
@@ -68,7 +68,7 @@ def get_input_groups():
ignore_index = inp["value"]
elif inp["name"] == "reduction":
reduction = inp["value"]
-
+
input_groups.append([input_tensor, target, weight, ignore_index, reduction])
return input_groups
diff --git a/benchmarks/NPUKernelBench/level2/2_GroupNormSwish.py b/benchmarks/NPUKernelBench/level2/2_GroupNormSwish.py
index feaf118e..35149dff 100644
--- a/benchmarks/NPUKernelBench/level2/2_GroupNormSwish.py
+++ b/benchmarks/NPUKernelBench/level2/2_GroupNormSwish.py
@@ -3,6 +3,7 @@
import torch_npu
import json
import os
+import random
class Model(nn.Module):
"""
diff --git a/hooks/skill_script_hook.py b/hooks/skill_script_hook.py
new file mode 100755
index 00000000..7c2bf526
--- /dev/null
+++ b/hooks/skill_script_hook.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python3
+"""
+Skill 脚本执行 Hook (PreToolUse 协议)。
+
+对 Bash 工具调用进行拦截:
+- 命中 INTERCEPTED_PATTERNS: 直接由本 hook 执行脚本,并以 PreToolUse 协议返回
+ permissionDecision=deny + additionalContext,把执行结果(exit_code/stdout/stderr)
+ 注入回 agent 的上下文,阻止 harness 二次执行。
+- 不命中: 输出 permissionDecision=allow,让 harness 正常执行(不在 hook 中重复执行)。
+
+输入:从 stdin 读取 harness PreToolUse JSON:
+ {"tool_name": "Bash", "tool_input": {"command": "..."}, ...}
+
+输出:单行 PreToolUse JSON 到 stdout。
+"""
+
+import json
+import os
+import re
+import subprocess
+import sys
+import time
+
+
+# 每个条目: (interpreter_regex, script_basename_regex)
+# 只匹配“某个解释器 + 脚本路径”这种真正的脚本调用,
+# 不匹配命令字符串里仅出现脚本名的情况(如 cat / grep / echo JSON 等)。
+_PY_INTERP = r"(?:python|python3|python3\.\d+)"
+_SH_INTERP = r"(?:bash|sh)"
+
+INTERCEPTED_PATTERNS = [
+ (_PY_INTERP, r"validate_tilelang_impl\.py"),
+ (_PY_INTERP, r"validate_ascendc_impl\.py"),
+ (_SH_INTERP, r"evaluate_tilelang\.sh"),
+ (_SH_INTERP, r"evaluate_ascendc\.sh"),
+ (_PY_INTERP, r"performance\.py"),
+ (_SH_INTERP, r"batch_run_performance\.sh"),
+ (_PY_INTERP, r"build_ascendc\.py"),
+ (_PY_INTERP, r"verification_ascendc\.py"),
+ (_PY_INTERP, r"verification_tilelang\.py"),
+]
+
+# 命令前缀允许的部分(环境变量赋值、cd ... &&、source ... &&)
+# 这里只是用于推断"真实的脚本调用",不需要覆盖所有 shell 语法。
+_LEADING_PREFIX = (
+ r"^\s*"
+ r"(?:export\s+)?" # 可选的 export 关键字
+ r"(?:[A-Za-z_][A-Za-z0-9_]*=\S+\s+)*" # ENV=VAL ...
+ r"(?:cd\s+\S+\s*&&\s*)?" # cd
&&
+ r"(?:export\s+)?" # 可选的 export 关键字
+ r"(?:[A-Za-z_][A-Za-z0-9_]*=\S+\s+)*" # 再次允许 ENV=VAL
+ r"(?:&&\s+)*" # 支持 && 链式连接
+)
+
+# 项目根目录:优先从环境变量获取,fallback 到 __file__ 计算
+# 避免 hook 被以相对路径调用时 __file__ 解析错误
+PROJECT_ROOT = os.environ.get("PROJECT_ROOT")
+if not PROJECT_ROOT:
+ PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+
+def should_intercept(command: str) -> bool:
+ # 仅当命令的"实际执行段"是 形式时才拦截。
+ # 兼容:环境变量前缀、可选的 `cd &&` 前缀。
+ for interp, script in INTERCEPTED_PATTERNS:
+ # 路径里允许出现 / 和非空白字符;脚本名用 \b 边界
+ pattern = (
+ _LEADING_PREFIX
+ + interp
+ + r"\s+"
+ + r"(?:\S*/)?"
+ + script
+ + r"(?:\s|$)"
+ )
+ if re.search(pattern, command):
+ return True
+ return False
+
+
+def emit_pretooluse(permission_decision: str, *, reason: str = "", additional_context: str = ""):
+ output = {
+ "hookSpecificOutput": {
+ "hookEventName": "PreToolUse",
+ "permissionDecision": permission_decision,
+ }
+ }
+ if reason:
+ output["hookSpecificOutput"]["permissionDecisionReason"] = reason
+ if additional_context:
+ output["hookSpecificOutput"]["additionalContext"] = additional_context
+ print(json.dumps(output, ensure_ascii=False), flush=True)
+
+
+def truncate(text: str, limit: int = 8000) -> str:
+ if not text:
+ return ""
+ if len(text) <= limit:
+ return text
+ head = text[: limit // 2]
+ tail = text[-limit // 2 :]
+ return f"{head}\n\n... [truncated {len(text) - limit} chars] ...\n\n{tail}"
+
+
+def execute_intercepted(command: str) -> None:
+ start_time = time.time()
+ cwd = os.getcwd()
+
+ # 解析路径:若是相对路径且在 cwd 不存在,则尝试 PROJECT_ROOT 下解析
+ # 这里我们直接通过 shell 执行原命令,但先确保 cwd 是 PROJECT_ROOT,
+ # 这样像 "python skills/..." 之类的相对路径能正确解析。
+ if os.path.isdir(PROJECT_ROOT):
+ cwd = PROJECT_ROOT
+
+ try:
+ proc = subprocess.run(
+ command,
+ shell=True,
+ cwd=cwd,
+ capture_output=True,
+ text=True,
+ timeout=1800,
+ )
+ exit_code = proc.returncode
+ stdout = proc.stdout or ""
+ stderr = proc.stderr or ""
+ except subprocess.TimeoutExpired as e:
+ exit_code = 124
+ stdout = (e.stdout.decode("utf-8", errors="replace") if e.stdout else "")
+ stderr = (e.stderr.decode("utf-8", errors="replace") if e.stderr else "")
+ stderr += "\n[HOOK] 命令执行超时(30 分钟)"
+ except Exception as e:
+ exit_code = 1
+ stdout = ""
+ stderr = f"[HOOK] 执行异常: {type(e).__name__}: {e}"
+
+ duration_ms = int((time.time() - start_time) * 1000)
+
+ additional_context = (
+ f"[skill_script_hook intercepted execution]\n"
+ f"command: {command}\n"
+ f"cwd: {cwd}\n"
+ f"exit_code: {exit_code}\n"
+ f"duration_ms: {duration_ms}\n"
+ f"--- stdout ---\n{truncate(stdout)}\n"
+ f"--- stderr ---\n{truncate(stderr)}\n"
+ )
+
+ status = "成功" if exit_code == 0 else "失败"
+ reason = (
+ f"[Hook 拦截提示] 该命令命中 skill_script_hook 拦截规则,已由 hook 代为执行({status},exit_code={exit_code})。"
+ f"执行结果见下方 additionalContext,原命令已被替换为 no-op,不会重复执行。"
+ )
+
+ # 使用 allow + updatedInput 将原命令替换为 no-op,避免 harness 显示 "Error:" 前缀
+ output = {
+ "hookSpecificOutput": {
+ "hookEventName": "PreToolUse",
+ "permissionDecision": "allow",
+ "permissionDecisionReason": reason,
+ "additionalContext": additional_context,
+ "updatedInput": {
+ "command": f"echo '[hook-noop] original command was intercepted and executed by skill_script_hook'"
+ },
+ }
+ }
+ print(json.dumps(output, ensure_ascii=False), flush=True)
+
+
+def read_stdin_command():
+ try:
+ raw = sys.stdin.read()
+ except Exception:
+ return None, None
+ if not raw or not raw.strip():
+ return None, None
+ try:
+ data = json.loads(raw)
+ except json.JSONDecodeError:
+ return None, None
+ if not isinstance(data, dict):
+ return None, None
+ tool_name = data.get("tool_name")
+ tool_input = data.get("tool_input") or {}
+ command = tool_input.get("command") if isinstance(tool_input, dict) else None
+ return tool_name, command
+
+
+def main():
+ tool_name, command = read_stdin_command()
+
+ # 没有命令或不是 Bash —— allow,由 harness 处理
+ if not command or tool_name != "Bash":
+ emit_pretooluse("allow")
+ return
+
+ if should_intercept(command):
+ execute_intercepted(command)
+ else:
+ # 不拦截 —— 让 harness 正常处理(不在此重复执行)
+ emit_pretooluse("allow")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/settings.json b/settings.json
new file mode 100644
index 00000000..1d5d3c44
--- /dev/null
+++ b/settings.json
@@ -0,0 +1,16 @@
+{
+ "hooks": {
+ "PreToolUse": [
+ {
+ "matcher": "Bash",
+ "hooks": [
+ {
+ "type": "command",
+ "command": "PROJECT_ROOT=/home/ascendC python3 /home/ascendC/.claude/hooks/skill_script_hook.py",
+ "timeout": 1830
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/skills/triton/kernel-verifier/scripts/validate_triton_impl.py b/skills/triton/kernel-verifier/scripts/validate_triton_impl.py
index d24180ad..27123fc8 100644
--- a/skills/triton/kernel-verifier/scripts/validate_triton_impl.py
+++ b/skills/triton/kernel-verifier/scripts/validate_triton_impl.py
@@ -137,6 +137,43 @@ def _resolve_call_name(node):
return None
+def _resolve_getattr_call(node):
+ """从 getattr(obj, "attr_name") 或 builtins.getattr(...) 调用中还原 (qualifier, attr)。
+
+ 仅处理第二个参数为字符串字面量的情况。
+ 返回 (qualifier, attr) 或 None。
+ """
+ if not isinstance(node, ast.Call):
+ return None
+ func = node.func
+ is_getattr = False
+ if isinstance(func, ast.Name) and func.id == "getattr":
+ is_getattr = True
+ elif isinstance(func, ast.Attribute) and func.attr == "getattr":
+ if isinstance(func.value, ast.Name) and func.value.id == "builtins":
+ is_getattr = True
+ if not is_getattr:
+ return None
+ args = node.args
+ if len(args) < 2:
+ return None
+ obj_node = args[0]
+ attr_node = args[1]
+ if not isinstance(attr_node, ast.Constant) or not isinstance(attr_node.value, str):
+ return None
+ attr_name = attr_node.value
+ if isinstance(obj_node, ast.Name):
+ return (obj_node.id, attr_name)
+ if isinstance(obj_node, ast.Attribute):
+ if isinstance(obj_node.value, ast.Name):
+ return (f"{obj_node.value.id}.{obj_node.attr}", attr_name)
+ if isinstance(obj_node.value, ast.Attribute):
+ inner = obj_node.value
+ if isinstance(inner.value, ast.Name):
+ return (f"{inner.value.id}.{inner.attr}.{obj_node.attr}", attr_name)
+ return None
+
+
def _get_subscript_value_name(node):
"""从 kernel[grid](...) 的 Subscript 节点提取 kernel 名称。"""
if isinstance(node, ast.Subscript):
@@ -241,6 +278,43 @@ def _count_kernel_launches_in_forward(forward_node):
return count
+def _check_single_call_violation(line, qual, attr):
+ """对单个 (qualifier, attr) 调用判断是否违规,返回 violation dict 或 None。"""
+ if qual == "torch":
+ if attr not in ALLOWED_TORCH_FUNCS:
+ return {
+ "line": line,
+ "call": f"torch.{attr}",
+ "reason": f"torch.{attr} 是计算操作,必须在 Triton kernel 中实现",
+ }
+ return None
+ if qual in ("F", "functional", "torch.nn.functional", "nn.functional"):
+ return {
+ "line": line,
+ "call": f"{qual}.{attr}",
+ "reason": f"{qual}.{attr} 是 PyTorch 计算操作,必须在 Triton kernel 中实现",
+ }
+ if qual == "triton" and attr in ALLOWED_TRITON_ATTRS:
+ return None
+ if attr in FORBIDDEN_TENSOR_METHODS:
+ if qual not in ("torch", "F", "triton", "functional", "torch.nn.functional", "nn.functional"):
+ return {
+ "line": line,
+ "call": f"{qual}.{attr}()" if qual else f"{attr}()",
+ "reason": f"{attr} 是计算操作,必须在 Triton kernel 中实现",
+ }
+ return None
+ if qual == "self":
+ if attr not in ("forward",):
+ return {
+ "line": line,
+ "call": f"self.{attr}(...)",
+ "reason": f"self.{attr}() 疑似 nn.Module 前向调用,核心计算必须在 Triton kernel 中实现",
+ }
+ return None
+ return None
+
+
def check_forbidden_torch_ops(forward_node):
"""检查 forward 中是否使用了禁止的 torch 计算操作或 Python 控制流。
@@ -306,51 +380,31 @@ def check_forbidden_torch_ops(forward_node):
qual, attr = resolved
- # --- torch.xxx(...) ---
- if qual == "torch":
- if attr not in ALLOWED_TORCH_FUNCS:
- violations.append({
- "line": node.lineno,
- "call": f"torch.{attr}",
- "reason": f"torch.{attr} 是计算操作,必须在 Triton kernel 中实现",
- })
- continue
-
- # --- F.xxx(...) / functional.xxx(...) ---
- if qual in ("F", "functional", "torch.nn.functional", "nn.functional"):
- violations.append({
- "line": node.lineno,
- "call": f"{qual}.{attr}",
- "reason": f"{qual}.{attr} 是 PyTorch 计算操作,必须在 Triton kernel 中实现",
- })
- continue
-
- # --- triton.cdiv 等 —— 允许 ---
- if qual == "triton" and attr in ALLOWED_TRITON_ATTRS:
- continue
-
- # --- tensor 方法计算操作 ---
- if attr in FORBIDDEN_TENSOR_METHODS:
- # 排除已知安全的 qual(torch/F/triton 已在上面处理)
- if qual not in ("torch", "F", "triton", "functional", "torch.nn.functional", "nn.functional"):
+ # --- getattr(obj, "attr") 反射绕过检测 ---
+ if attr == "getattr" and qual in (None, "builtins"):
+ getattr_resolved = _resolve_getattr_call(node)
+ if getattr_resolved is not None:
+ gqual, gattr = getattr_resolved
+ getattr_violation = _check_single_call_violation(
+ node.lineno, gqual, gattr
+ )
+ if getattr_violation is not None:
+ getattr_violation["call"] = (
+ f"getattr({gqual}, '{gattr}')"
+ )
+ violations.append(getattr_violation)
+ else:
violations.append({
"line": node.lineno,
- "call": f"{qual}.{attr}()" if qual else f"{attr}()",
- "reason": f"{attr} 是计算操作,必须在 Triton kernel 中实现",
+ "call": "getattr(...)",
+ "reason": "getattr() 动态属性访问疑似绕过检测,forward() 中禁止使用 getattr 调用 PyTorch 接口",
})
continue
- # --- self.layer_name(x) —— 禁止 nn.Module 调用 ---
- if qual == "self":
- # 允许 self.forward() 递归,以及属性访问不在这里(ast.Attribute 不是 Call)
- # self.xxx(...) 形式视为 nn.Module 前向调用
- if attr not in ("forward",):
- violations.append({
- "line": node.lineno,
- "call": f"self.{attr}(...)",
- "reason": f"self.{attr}() 疑似 nn.Module 前向调用,核心计算必须在 Triton kernel 中实现",
- })
- continue
+ violation = _check_single_call_violation(node.lineno, qual, attr)
+ if violation is not None:
+ violations.append(violation)
+ continue
# --- 规则 B: 如果 forward() 中 kernel 启动次数 > 1,视为 Type3 退化 ---
if kernel_launch_count > 1:
diff --git a/skills/triton/latency-optimizer/.DS_Store b/skills/triton/latency-optimizer/.DS_Store
new file mode 100644
index 00000000..40660e26
Binary files /dev/null and b/skills/triton/latency-optimizer/.DS_Store differ
diff --git a/skills/triton/latency-optimizer/SKILL.md b/skills/triton/latency-optimizer/SKILL.md
index 8a35630d..52b76dac 100644
--- a/skills/triton/latency-optimizer/SKILL.md
+++ b/skills/triton/latency-optimizer/SKILL.md
@@ -160,7 +160,7 @@ for n in range(N):
sum_val += val # 标量加法
# 特征 3:标量控制流
-if x > 0: # 标量条件,导致 warp divergence
+if x > 0: # 标量条件,导致 SIMD 分支分化
result = tl.exp(x)
else:
result = tl.cos(x)
diff --git a/skills/triton/latency-optimizer/references/autotune.md b/skills/triton/latency-optimizer/references/autotune.md
index 25b3d7f6..8b61cd37 100644
--- a/skills/triton/latency-optimizer/references/autotune.md
+++ b/skills/triton/latency-optimizer/references/autotune.md
@@ -4,7 +4,7 @@
Triton autotune 用于自动选择最优的 kernel 配置参数,主要包括影响分核(split)和切块(tiling)大小的参数,主要使用方式如下:
-**三种种使用方式:**
+**三种使用方式:**
| 方式 | 说明 | 适用场景 |
|------|------|---------|
@@ -114,8 +114,8 @@ triton.Config(
| `kwargs` | ✅ | ✅ | 完全支持 |
| `num_warps` | ✅ | ❌ | NPU 架构差异,不支持 |
| `num_stages` | ✅ | ❌ | NPU 架构差异,不支持 |
-| `multibuffer` | ❌ | ✅ | NPU 特有,多缓冲优化 |
-| `unit_flag` | ❌ | ✅ | NPU 特有,独立计算单元 |
+| `multibuffer` | ❌ | ✅ | NPU 特有,多缓冲优化(通过 kwargs 传入) |
+| `unit_flag` | ❌ | ✅ | NPU 特有,独立计算单元(通过 kwargs 传入) |
#### 使用示例
@@ -227,13 +227,19 @@ pid = tl.program_id(0)
offs_m = pid * BLOCK_M + tl.arange(0, BLOCK_M)# 可以知道BLOCK_M 是 split 参数
mask_m = offs_m < n_rows
-# 二维切分
-pid_m = tl.program_id(0)
-pid_n = tl.program_id(1)
-offs_m = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)[:, None]# 可以知道BLOCK_M 是 split 参数
-offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)[None, :]# 可以知道BLOCK_N 是 split 参数
-mask_m = offs_m < n_rows
-mask_n = offs_n < n_cols
+# 二维切分(Ascend NPU 必须使用一维 Grid,通过一维 pid 映射到二维坐标)
+pid = tl.program_id(0)
+NUM_BLOCKS_M = tl.cdiv(M, BLOCK_M)
+NUM_BLOCKS_N = tl.cdiv(N, BLOCK_N)
+NUM_BLOCKS = NUM_BLOCKS_M * NUM_BLOCKS_N
+for block_idx in range(pid, NUM_BLOCKS, CORE_NUM): # 交错循环,每个核处理多个块
+ pid_m = block_idx // NUM_BLOCKS_N # 行块索引
+ pid_n = block_idx % NUM_BLOCKS_N # 列块索引
+ # BLOCK_M 和 BLOCK_N 都是 split 参数
+ offs_m = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)[:, None]
+ offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)[None, :]
+ mask_m = offs_m < M
+ mask_n = offs_n < N
```
#### step1.2.识别分块参数
@@ -365,7 +371,7 @@ hints 参数说明:
* 通过 `hints` 来显示指定轴关系时,`split_params`、`tiling_params`、`low_dim_axes`、`reduction_axes` 必须传入,即使某些参数为空
* 合法的轴名称是 `x/y/z/w/v/t`,仅仅用做关系映射
* `split_params` 和 `tiling_params` 为自动生成 tiling 算法必须的输入,`low_dim_axes` 和 `reduction_axes` 为 tiling 算法的可选输入,用于优化 tiling 效果,留空时 tiling 也能够自动生成,但可能会影响生成的候选 tiling 数量和质量
- * 当用户传入的 configs 不为空时,`auto_gen_config` 默认值为 `False`,如果希望此时也希望自动生成 tiling 配置并与用户传入的 configs 合并,需要显式在 `hints` 中传如入 `"auto_gen_config": True`
+ * 当用户传入的 configs 不为空时,`auto_gen_config` 默认值为 `False`,如果希望此时也希望自动生成 tiling 配置并与用户传入的 configs 合并,需要显式在 `hints` 中传入 `"auto_gen_config": True`
使用示例:
```python
@@ -779,4 +785,4 @@ export TRITON_PRINT_AUTOTUNING=1
**限制:** 进阶用法仅支持 Vector 类算子,不支持 Cube 类算子。
-**优先级:** 自定义 autotune > 半自动 autotune (hints) > 自定义 autotune
+**优先级:** 自动 autotune > 半自动 autotune (hints) > 自定义 autotune
diff --git a/skills/triton/latency-optimizer/references/avoid_scalar_lowering.md b/skills/triton/latency-optimizer/references/avoid_scalar_lowering.md
index 01ccbf53..01c0309c 100644
--- a/skills/triton/latency-optimizer/references/avoid_scalar_lowering.md
+++ b/skills/triton/latency-optimizer/references/avoid_scalar_lowering.md
@@ -44,12 +44,12 @@
---
-3. 扩展乘法(vmulext)
+4. 扩展乘法(vmulext)
始终降级。该操作在 IR 层面只支持 i32,而 i32 触发降级,所以实际上没有向量硬件支持。
---
-4. 累积操作(cumsum / cumprod)
+5. 累积操作(cumsum / cumprod)
```
┌───────────────────────────────────┬────────────────────┬──────────────────────┬──────────────┐
│ 数据类型 │ 累积维度是最后维度 │ 累积维度不是最后维度 │ 多个累积维度 │
@@ -65,7 +65,7 @@
- 多个累积维度不会触发降级(但可能有其他限制)。
---
-5. 归约操作(reduce)
+6. 归约操作(reduce)
归约的降级条件取决于芯片架构:
diff --git a/skills/triton/latency-optimizer/references/checklist.md b/skills/triton/latency-optimizer/references/checklist.md
index 4f8b1e98..c027cbda 100644
--- a/skills/triton/latency-optimizer/references/checklist.md
+++ b/skills/triton/latency-optimizer/references/checklist.md
@@ -15,7 +15,7 @@
- [ ] 对于除法操作,在不影响精度的情况下,必须使用 fp32 或 int32 数据类型进行计算
### 4. 模运算规范
-- [ ] 禁止直接使用 `a % b` 操作,必须使用 `a - (a / b) * b` 操作替代
+- [ ] 禁止直接使用 `a % b` 操作,必须使用 `a - (a // b) * b` 操作替代
### 5. Grid 并行度规范
- [ ] grid 并行数量禁止超过物理核数:
diff --git a/skills/triton/latency-optimizer/references/discrete_memory_access.md b/skills/triton/latency-optimizer/references/discrete_memory_access.md
index ea309e5c..486d09f8 100644
--- a/skills/triton/latency-optimizer/references/discrete_memory_access.md
+++ b/skills/triton/latency-optimizer/references/discrete_memory_access.md
@@ -2,7 +2,7 @@
## 概述
-在 Triton NPU kernel 中,当线程通过非连续或不可预测的索引向量访问全局内存时,会导致访存效率低下,显著降低带宽利用率。先将整块数据读取到share memory,再取非连续或不可预测的索引可以显著提升计算效率。
+在 Triton NPU kernel 中,当线程通过非连续或不可预测的索引向量访问全局内存时,会导致访存效率低下,显著降低带宽利用率。先将整块数据读取到UB(Unified Buffer),再取非连续或不可预测的索引可以显著提升计算效率。
## 触发条件
@@ -160,14 +160,14 @@ val = tl.load(x_ptr + offset + idx * stride_x, mask=mask) # 直接从global中
offset = tl.load(offset_ptr) # offset是一个完全无法预测的随机标量
idx = tl.load(idx_ptr + rn * stride_idx) # idx是一个完全无法预测的随机值向量
rm = tl.arange(0, M) # rm包含了所有的值,M为x张量的总长度
-x_shared = tl.load(x_ptr + offset_ptr + rm * stride_x) # 将x对应偏移的所有数据从global搬至share
-val = tl.gather(x_shared.to(tl.float16), idx, 0).to(tl.int32) # 再从share中select目标值,注意数据类型的切换
+x_shared = tl.load(x_ptr + offset + rm * stride_x) # 将x对应偏移的所有数据从global搬至UB
+val = tl.gather(x_shared.to(tl.float16), idx, 0).to(tl.int32) # 再从UB中select目标值,注意数据类型的切换
```
### 关键点
1. **识别无法预测的随机值**:溯源`tl.load`的输入索引计算过程,找到是否有无法预测的随机值,例如被`tl.load`读进来的值
-2. **自动优化**:将 `tl.load`的输入指针中的随机值剔除,改为读取一大块内存(注意不能超过share memory限制),然后使用`tl.gather`输入随机值,得到最终需要取的值
+2. **自动优化**:将 `tl.load`的输入指针中的随机值剔除,改为读取一大块内存(注意不能超过UB限制),然后使用`tl.gather`输入随机值,得到最终需要取的值
3. **注意gather的数据类型**:`tl.gather`不支持`int类型`,最好将输入强转成`tl.float16`再执行`tl.gather`,最后再转回原有的数据类型。如果提示精度报错,可以尝试强转成`tl.float32`。
### 模式 2:循环内通过随机索引访问小查找表
diff --git a/skills/triton/latency-optimizer/references/load-order.md b/skills/triton/latency-optimizer/references/load-order.md
index 9048c7e8..1d064ec8 100644
--- a/skills/triton/latency-optimizer/references/load-order.md
+++ b/skills/triton/latency-optimizer/references/load-order.md
@@ -70,8 +70,6 @@ def AB_load_kernel(
tl.store(p_O, b_O)
# store B
- idx_B = tl.load(p_B_index)
- p_B = B + idx_B
tl.store(p_B, b_B)
```
diff --git a/skills/triton/latency-optimizer/references/pass-merge.md b/skills/triton/latency-optimizer/references/pass-merge.md
index 1d6864a2..af31fa82 100644
--- a/skills/triton/latency-optimizer/references/pass-merge.md
+++ b/skills/triton/latency-optimizer/references/pass-merge.md
@@ -140,7 +140,6 @@ for col_offset in range(0, n_cols, BLOCK_SIZE):
| 1 | ~1 ms | 无循环开销 |
| 10 | ~10 ms | 线性增长 |
| 100 | ~100 ms | 线性增长 |
-| 512 | ~700 ms | **20x 慢于无循环版本** |
**原因分析**:
1. **循环展开有限**: 编译器不会激进展开所有循环
diff --git a/skills/triton/latency-optimizer/references/scalar_to_vector.md b/skills/triton/latency-optimizer/references/scalar_to_vector.md
index d182d3a9..e8d3c4d5 100644
--- a/skills/triton/latency-optimizer/references/scalar_to_vector.md
+++ b/skills/triton/latency-optimizer/references/scalar_to_vector.md
@@ -116,9 +116,8 @@ def count_2d(topk_ids_ptr, expert_num_tokens_ptr, num_experts: tl.constexpr,
**原始代码(scalar 控制流)**
```python
-# 莫格
x = tl.load(x_ptr + offsets, mask=mask)
-if x > 0: # 标量条件,导致 warp divergence
+if x > 0: # 标量条件,导致 SIMD 分支分化
result = tl.exp(x)
else:
result = tl.cos(x)
@@ -130,17 +129,16 @@ else:
x = tl.load(x_ptr + offsets, mask=mask)
cond_mask = x > 0 # vector 比较,返回布尔向量
exp_result = tl.exp(x)
-log_result = tl.cos(x)
-result = cond_mask * exp_result + ~cond_mask * log_result # vector 加法,无分支
+cos_result = tl.cos(x)
+result = cond_mask * exp_result + ~cond_mask * cos_result # vector 加法,无分支
```
**场景二:两个分支的计算逻辑定义域不一样,至少有一个会出现nan或inf等无效输出**
**原始代码(scalar 控制流)**
```python
-# 莫格
x = tl.load(x_ptr + offsets, mask=mask)
-if x > 0: # 标量条件,导致 warp divergence
+if x > 0: # 标量条件,导致 SIMD 分支分化
result = tl.exp(x)
else:
result = tl.log(x)
@@ -268,7 +266,7 @@ d = a - (a // b) * b # 公式转换
- **标量广播优化**:10-20% 加速,通过消除标量指令开销
- **标量规约优化**:5-128 倍加速(取决于 vector 并行度,FP16 理论加速比 128 倍)
-- **控制流优化**:消除 warp divergence,提升 SIMD 利用率至 90%+
+- **控制流优化**:消除 SIMD 分支分化,提升 Vector 利用率至 90%+
- **整体 kernel 优化**:在 LayerNorm、Softmax 等带宽受限算子中,端到端性能提升 2-3 倍
**实测数据参考**:在 LayerNorm 算子中,将 mean/variance 计算从标量累加改为 vector 分块规约,UB 利用率从 35% 提升至 78%,kernel 执行时间减少 62%。
diff --git a/skills/triton/latency-optimizer/references/vector_core_partition.md b/skills/triton/latency-optimizer/references/vector_core_partition.md
index bbbe641f..41c3eb7c 100644
--- a/skills/triton/latency-optimizer/references/vector_core_partition.md
+++ b/skills/triton/latency-optimizer/references/vector_core_partition.md
@@ -366,7 +366,6 @@ def softmax_kernel(
| 策略 | 适用场景 | Grid 大小 |
|------|---------|---------|
| **一维分核** | 单维度处理(如逐行) | min(N / BLOCK, num_cores) |
-| **二维分核** | 矩阵运算(如 matmul) | (M / BM, N / BN) |
| **多行并行** | 行级 reduce(如 softmax) | min(M / ROWS_PER_BLOCK, num_cores) |
### 选择依据