ops-tune-workbench-plugin 0.0.0-stage → 0.0.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/NOTICE.md +14 -0
  2. package/README.md +39 -2
  3. package/cordis.patch.yml +20 -0
  4. package/lib/client.js +4253 -0
  5. package/lib/host.js +2518 -0
  6. package/package.json +53 -4
  7. package/skills/tool-ops-tune/INSTALL.md +52 -0
  8. package/skills/tool-ops-tune/README.md +167 -0
  9. package/skills/tool-ops-tune/SKILL.md +93 -0
  10. package/skills/tool-ops-tune/agent/AGENTS.md +195 -0
  11. package/skills/tool-ops-tune/agent/agents/perf-implementer.md +68 -0
  12. package/skills/tool-ops-tune/agent/agents/perf-reviewer.md +86 -0
  13. package/skills/tool-ops-tune/agent/workflows/task-prompts.md +362 -0
  14. package/skills/tool-ops-tune/agent/workflows/troubleshooting.md +43 -0
  15. package/skills/tool-ops-tune/docs/figures/opstune-architecture.png +0 -0
  16. package/skills/tool-ops-tune/docs/figures/opstune-architecture.svg +253 -0
  17. package/skills/tool-ops-tune/docs/ops-tune-design.md +404 -0
  18. package/skills/tool-ops-tune/docs//347/224/250/344/276/213/346/236/204/345/273/272.md +714 -0
  19. package/skills/tool-ops-tune/docs//347/237/245/350/257/206/345/272/223/350/247/204/350/214/203.md +103 -0
  20. package/skills/tool-ops-tune/evals/evals.json +242 -0
  21. package/skills/tool-ops-tune/ops-collect/SKILL.md +146 -0
  22. package/skills/tool-ops-tune/ops-collect/references/tool-routing.md +86 -0
  23. package/skills/tool-ops-tune/ops-collect/scripts/collect_board.sh +444 -0
  24. package/skills/tool-ops-tune/ops-collect/scripts/collect_cases.py +368 -0
  25. package/skills/tool-ops-tune/ops-collect/scripts/collect_sim.sh +134 -0
  26. package/skills/tool-ops-tune/ops-collect/scripts/csv_to_db.py +314 -0
  27. package/skills/tool-ops-tune/ops-collect/scripts/db_bench.py +545 -0
  28. package/skills/tool-ops-tune/ops-collect/scripts/facts_bridge.py +1063 -0
  29. package/skills/tool-ops-tune/ops-collect/scripts/isolation_test.py +454 -0
  30. package/skills/tool-ops-tune/ops-collect/scripts/jsonl_to_cases.py +163 -0
  31. package/skills/tool-ops-tune/ops-family/SKILL.md +76 -0
  32. package/skills/tool-ops-tune/ops-family/references/broadcast/advanced_tiling.md +92 -0
  33. package/skills/tool-ops-tune/ops-family/references/broadcast/broadcast_design.md +160 -0
  34. package/skills/tool-ops-tune/ops-family/references/broadcast/code/broadcast_add_kernel.cpp +126 -0
  35. package/skills/tool-ops-tune/ops-family/references/broadcast/code/broadcast_add_tiling.cpp +144 -0
  36. package/skills/tool-ops-tune/ops-family/references/broadcast/code/broadcast_tiling_common.h +401 -0
  37. package/skills/tool-ops-tune/ops-family/references/broadcast/code/onedim_add_kernel.cpp +107 -0
  38. package/skills/tool-ops-tune/ops-family/references/broadcast/code/onedim_add_tiling.cpp +64 -0
  39. package/skills/tool-ops-tune/ops-family/references/broadcast/datacopypad_design.md +46 -0
  40. package/skills/tool-ops-tune/ops-family/references/broadcast/guide.md +14 -0
  41. package/skills/tool-ops-tune/ops-family/references/broadcast/nddma_design.md +53 -0
  42. package/skills/tool-ops-tune/ops-family/references/broadcast/onedim_design.md +114 -0
  43. package/skills/tool-ops-tune/ops-family/references/broadcast/ub_broadcast_design.md +67 -0
  44. package/skills/tool-ops-tune/ops-family/references/common/datacopy_optimization_design.md +351 -0
  45. package/skills/tool-ops-tune/ops-family/references/common/index.md +9 -0
  46. package/skills/tool-ops-tune/ops-family/references/common/tail_block_design.md +268 -0
  47. package/skills/tool-ops-tune/ops-family/references/common/timing_harness/index.md +38 -0
  48. package/skills/tool-ops-tune/ops-family/references/common/timing_harness/timing_harness_aclrt_event.cpp +199 -0
  49. package/skills/tool-ops-tune/ops-family/references/common/timing_harness/timing_harness_torch_event.py +130 -0
  50. package/skills/tool-ops-tune/ops-family/references/common/ub_resident_design.md +342 -0
  51. package/skills/tool-ops-tune/ops-family/references/conversion/guide.md +69 -0
  52. package/skills/tool-ops-tune/ops-family/references/conversion/index.md +4 -0
  53. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_base.template +81 -0
  54. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_big_dim.template +217 -0
  55. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_cut_one_axis.template +336 -0
  56. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_cut_two_axis.template +781 -0
  57. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_n_last.template +340 -0
  58. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_small_shape.template +321 -0
  59. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_tensor_move.template +102 -0
  60. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_transdata_5hd.template +480 -0
  61. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_transdata_5hd_021axis.template +533 -0
  62. package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_with_gather.template +688 -0
  63. package/skills/tool-ops-tune/ops-family/references/conversion/templates/transpose_tiling_data.template +192 -0
  64. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_big_dim.md +220 -0
  65. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_cut_one_axis.md +175 -0
  66. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_cut_two_axis.md +177 -0
  67. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_fusion_design.md +398 -0
  68. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_gather.md +191 -0
  69. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_n_last.md +276 -0
  70. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_small_shape.md +315 -0
  71. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_tensor_move.md +203 -0
  72. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_tiling_data.md +176 -0
  73. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_vconv_021.md +206 -0
  74. package/skills/tool-ops-tune/ops-family/references/conversion/transpose_vconv_5hd.md +179 -0
  75. package/skills/tool-ops-tune/ops-family/references/elementwise/double_buffer_design.md +288 -0
  76. package/skills/tool-ops-tune/ops-family/references/elementwise/guide.md +46 -0
  77. package/skills/tool-ops-tune/ops-family/references/elementwise/index.md +10 -0
  78. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/CMakeLists.txt +131 -0
  79. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/README.md +55 -0
  80. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_extension/add_custom_torch.cpp +80 -0
  81. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_extension/ops.h +23 -0
  82. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_extension/register.cpp +30 -0
  83. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_host/add_custom.asc +149 -0
  84. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_host/data_utils.h +97 -0
  85. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_kernel/add_custom_kernel.asc +136 -0
  86. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_kernel/add_custom_tiling.h +31 -0
  87. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/run.sh +86 -0
  88. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/scripts/gen_data.py +70 -0
  89. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/scripts/golden.py +34 -0
  90. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/scripts/test_torch.py +99 -0
  91. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/scripts/verify_result.py +56 -0
  92. package/skills/tool-ops-tune/ops-family/references/elementwise/templates/usage_guide.md +61 -0
  93. package/skills/tool-ops-tune/ops-family/references/elementwise/vector_efficiency_design.md +203 -0
  94. package/skills/tool-ops-tune/ops-family/references/fa/fa_mixed_kernel_design.md +174 -0
  95. package/skills/tool-ops-tune/ops-family/references/fa/fa_roofline_design.md +141 -0
  96. package/skills/tool-ops-tune/ops-family/references/fa/guide.md +87 -0
  97. package/skills/tool-ops-tune/ops-family/references/knowledge_gaps.json +56 -0
  98. package/skills/tool-ops-tune/ops-family/references/matmul/best-practices-guide.md +14 -0
  99. package/skills/tool-ops-tune/ops-family/references/matmul/fullload_design.md +344 -0
  100. package/skills/tool-ops-tune/ops-family/references/matmul/guide.md +52 -0
  101. package/skills/tool-ops-tune/ops-family/references/matmul/mte2_preload_design.md +320 -0
  102. package/skills/tool-ops-tune/ops-family/references/matmul/pingpong_design.md +254 -0
  103. package/skills/tool-ops-tune/ops-family/references/matmul/scale_coalescing_design.md +282 -0
  104. package/skills/tool-ops-tune/ops-family/references/matmul/streamk_design.md +374 -0
  105. package/skills/tool-ops-tune/ops-family/references/matmul/swat_design.md +423 -0
  106. package/skills/tool-ops-tune/ops-family/references/matmul/templates/matmul_basic_blaze.template +165 -0
  107. package/skills/tool-ops-tune/ops-family/references/matmul/templates/usage_guide.md +29 -0
  108. package/skills/tool-ops-tune/ops-family/references/mc2/best-practices-guide.md +43 -0
  109. package/skills/tool-ops-tune/ops-family/references/mc2/guide.md +42 -0
  110. package/skills/tool-ops-tune/ops-family/references/mc2/local_matmul_design.md +130 -0
  111. package/skills/tool-ops-tune/ops-family/references/mc2/pipeline_balancing_design.md +502 -0
  112. package/skills/tool-ops-tune/ops-family/references/reduce/guide.md +178 -0
  113. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/kernel_utils.template +75 -0
  114. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ar_full_load.template +429 -0
  115. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ar_recompute.template +485 -0
  116. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ar_small_r.template +348 -0
  117. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ara_full_load.template +574 -0
  118. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ara_online.template +324 -0
  119. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ara_recompute.template +436 -0
  120. package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_base.template +950 -0
  121. package/skills/tool-ops-tune/ops-family/references/reduce/templates/fused_attention_online_softmax_design.md +428 -0
  122. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ar_full_load.md +137 -0
  123. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ar_recompute.md +135 -0
  124. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ar_small_r.md +122 -0
  125. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ara_full_load.md +144 -0
  126. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ara_online.md +185 -0
  127. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ara_recompute.md +163 -0
  128. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_tiling.template +460 -0
  129. package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_tiling_data.template +278 -0
  130. package/skills/tool-ops-tune/ops-family/references/reduce/templates/state_resident_design.md +337 -0
  131. package/skills/tool-ops-tune/ops-family/references/reduce/templates/usage_guide.md +253 -0
  132. package/skills/tool-ops-tune/ops-family/references/scalar/coding_principles.md +476 -0
  133. package/skills/tool-ops-tune/ops-family/references/scalar/conv_scalar.md +163 -0
  134. package/skills/tool-ops-tune/ops-family/references/scalar/guide.md +30 -0
  135. package/skills/tool-ops-tune/ops-family/references/simt/guide.md +91 -0
  136. package/skills/tool-ops-tune/ops-family/references/simt/optimization-guide.md +110 -0
  137. package/skills/tool-ops-tune/ops-family/references/sort/guide.md +83 -0
  138. package/skills/tool-ops-tune/ops-family/references/sort/radix_sort.md +354 -0
  139. package/skills/tool-ops-tune/ops-family/scripts/family_asset_audit.py +68 -0
  140. package/skills/tool-ops-tune/ops-family/scripts/tiling_candidates.py +419 -0
  141. package/skills/tool-ops-tune/ops-mechanism/SKILL.md +23 -0
  142. package/skills/tool-ops-tune/ops-mechanism/cases/ascendc/case_groupedmatmul_pipeline.md +23 -0
  143. package/skills/tool-ops-tune/ops-mechanism/cases/ascendc/case_triton_atomic_revert.md +19 -0
  144. package/skills/tool-ops-tune/ops-mechanism/cases/mc2/case_mc2_fusion.md +19 -0
  145. package/skills/tool-ops-tune/ops-mechanism/references/optimize/api-usage.md +70 -0
  146. package/skills/tool-ops-tune/ops-mechanism/references/optimize/ascendc-regbase-vf.md +45 -0
  147. package/skills/tool-ops-tune/ops-mechanism/references/optimize/ascendc-tiling.md +51 -0
  148. package/skills/tool-ops-tune/ops-mechanism/references/optimize/data-copy.md +67 -0
  149. package/skills/tool-ops-tune/ops-mechanism/references/optimize/index.md +45 -0
  150. package/skills/tool-ops-tune/ops-mechanism/references/optimize/mc2-balancing.md +77 -0
  151. package/skills/tool-ops-tune/ops-mechanism/references/optimize/mc2.md +103 -0
  152. package/skills/tool-ops-tune/ops-mechanism/references/optimize/memory-hierarchy.md +40 -0
  153. package/skills/tool-ops-tune/ops-mechanism/references/optimize/pipeline.md +95 -0
  154. package/skills/tool-ops-tune/ops-mechanism/scripts/case_backfill.py +170 -0
  155. package/skills/tool-ops-tune/ops-mechanism/scripts/log_gap.py +145 -0
  156. package/skills/tool-ops-tune/ops-mechanism/scripts/mc2_search.py +367 -0
  157. package/skills/tool-ops-tune/ops-report/SKILL.md +77 -0
  158. package/skills/tool-ops-tune/ops-verdict/evals/run_evals.py +837 -0
  159. package/skills/tool-ops-tune/ops-verdict/evals/smoke/install-hooks.sh +65 -0
  160. package/skills/tool-ops-tune/ops-verdict/evals/smoke/run_smoke.sh +405 -0
  161. package/skills/tool-ops-tune/ops-verdict/fixtures/core_tail_a5.csv +25 -0
  162. package/skills/tool-ops-tune/ops-verdict/fixtures/few_cores_a5.csv +5 -0
  163. package/skills/tool-ops-tune/ops-verdict/fixtures/mte2_bound_a2.csv +17 -0
  164. package/skills/tool-ops-tune/ops-verdict/fixtures/multi_kernel_mix_a5.csv +7 -0
  165. package/skills/tool-ops-tune/ops-verdict/fixtures/no_bound_a5.csv +9 -0
  166. package/skills/tool-ops-tune/ops-verdict/fixtures/serial_no_overlap_a2.csv +9 -0
  167. package/skills/tool-ops-tune/ops-verdict/fixtures/vec_bound_a5.csv +9 -0
  168. package/skills/tool-ops-tune/ops-verdict/references/io-contracts.md +94 -0
  169. package/skills/tool-ops-tune/ops-verdict/references/rule-schema.md +130 -0
  170. package/skills/tool-ops-tune/ops-verdict/references/scope-schema.md +61 -0
  171. package/skills/tool-ops-tune/ops-verdict/references/soc-spec.yaml +140 -0
  172. package/skills/tool-ops-tune/ops-verdict/rules/L0-instruction.yaml +67 -0
  173. package/skills/tool-ops-tune/ops-verdict/rules/L1-single-core.yaml +316 -0
  174. package/skills/tool-ops-tune/ops-verdict/rules/L2-inter-core.yaml +173 -0
  175. package/skills/tool-ops-tune/ops-verdict/rules/L3-inter-card.yaml +156 -0
  176. package/skills/tool-ops-tune/ops-verdict/scripts/batch_pipeline.py +444 -0
  177. package/skills/tool-ops-tune/ops-verdict/scripts/calibrate.py +249 -0
  178. package/skills/tool-ops-tune/ops-verdict/scripts/delta_compare.py +244 -0
  179. package/skills/tool-ops-tune/ops-verdict/scripts/render_html.py +274 -0
  180. package/skills/tool-ops-tune/ops-verdict/scripts/snapshot.py +200 -0
  181. package/skills/tool-ops-tune/ops-verdict/scripts/trace_bubble.py +331 -0
  182. package/skills/tool-ops-tune/ops-verdict/scripts/verdict.py +1072 -0
  183. package/skills/tool-ops-tune/scripts/diff_audit.py +281 -0
@@ -0,0 +1,244 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # ----------------------------------------------------------------------------------------------------------
4
+ # Copyright (c) 2026 Huawei Technologies Co., Ltd.
5
+ # This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6
+ # CANN Open Software License Agreement Version 2.0 (the "License").
7
+ # Please refer to the License for details. You may not use this file except in compliance with the License.
8
+ # THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9
+ # INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10
+ # See LICENSE in the root of the software repository for the full text of the License.
11
+ # ----------------------------------------------------------------------------------------------------------
12
+ """delta_compare.py — 前后对比硬校验(口径一致性 + 精度门禁 + 噪声带三段结论)
13
+
14
+ 设计对齐(docs/算子调优AGENT设计文档.md §6 数据契约):
15
+ - HARD_KEYS 口径不一致 → exit 2 拒绝比较(性能数字在口径漂移下无意义)。
16
+ - MECHANISM_KEYS(tiling_key/block_dim)默认 WARN 写入报告;--strict 恢复禁比
17
+ (分核/tiling 类优化本身就会改变这两个字段——这是对优化本质的建模)。
18
+ - 精度门禁(第三道,脚本化):任一侧 precision != pass → exit 2。
19
+ - 噪声带三段结论(实测纪律):±5% 内不判收益也不判劣化 → 加采样轮次。
20
+
21
+ 用法:
22
+ python3 delta_compare.py --baseline baseline/result.json --after round-01/result.json \
23
+ --output comparison.md
24
+ """
25
+
26
+ from __future__ import annotations
27
+
28
+ import argparse
29
+ import datetime
30
+ import json
31
+ import sys
32
+ from pathlib import Path
33
+
34
+ HARD_KEYS = (
35
+ "op",
36
+ "soc",
37
+ "mode",
38
+ "timing_method",
39
+ "baseline_kind",
40
+ "cann_version",
41
+ "device_id",
42
+ "shape",
43
+ "dtype",
44
+ "format",
45
+ "warmup",
46
+ "repeat",
47
+ )
48
+ MECHANISM_KEYS = ("tiling_key", "block_dim")
49
+ NOISE_BAND = 0.05 # ±5%:噪声带内不归因(run 间噪声 >5% 时小于噪声的收益不得宣称)
50
+
51
+
52
+ def load(p: str) -> dict:
53
+ return json.loads(Path(p).read_text(encoding="utf-8"))
54
+
55
+
56
+ def fmt_speedup(b, a):
57
+ if b and a and a > 0:
58
+ return b / a
59
+ return None
60
+
61
+
62
+ def main() -> int:
63
+ ap = argparse.ArgumentParser(description="前后对比硬校验")
64
+ ap.add_argument(
65
+ "--baseline", required=True, help="snapshot.py 产出的基线 result.json"
66
+ )
67
+ ap.add_argument("--after", required=True, help="优化后 result.json(同口径)")
68
+ ap.add_argument("--output", required=True, help="comparison.md 输出")
69
+ ap.add_argument(
70
+ "--strict",
71
+ action="store_true",
72
+ help="机制变量(tiling_key/block_dim)差异也算口径不一致",
73
+ )
74
+ ap.add_argument("--noise-band", type=float, default=NOISE_BAND)
75
+ args = ap.parse_args()
76
+
77
+ base, after = load(args.baseline), load(args.after)
78
+
79
+ # ---- 门禁 1:口径硬校验 ----
80
+ hard = tuple(HARD_KEYS) + (MECHANISM_KEYS if args.strict else ())
81
+ diffs = [
82
+ f"{k}: {base.get(k)!r} != {after.get(k)!r}"
83
+ for k in hard
84
+ if base.get(k) is not None
85
+ and after.get(k) is not None
86
+ and base.get(k) != after.get(k)
87
+ ]
88
+ if diffs:
89
+ print(
90
+ "ERROR: 基线与优化后口径不一致,禁止比较:\n " + "\n ".join(diffs),
91
+ file=sys.stderr,
92
+ )
93
+ print("任一口径字段变化都应重建基线(跨口径比较禁令)。", file=sys.stderr)
94
+ return 2
95
+
96
+ # ---- 门禁 2:精度门禁(第三道)----
97
+ if base.get("precision") != "pass" or after.get("precision") != "pass":
98
+ print(
99
+ "ERROR: 只有精度均为 pass 的结果才能生成性能结论 "
100
+ f"(baseline={base.get('precision')}, after={after.get('precision')})",
101
+ file=sys.stderr,
102
+ )
103
+ return 2
104
+
105
+ # 机制变量 WARN ----
106
+ mech_warns = [
107
+ f"{k}: {base.get(k)!r} → {after.get(k)!r}"
108
+ for k in MECHANISM_KEYS
109
+ if base.get(k) is not None
110
+ and after.get(k) is not None
111
+ and base.get(k) != after.get(k)
112
+ ]
113
+
114
+ b_us, a_us = base.get("kernel_avg_us"), after.get("kernel_avg_us")
115
+ speedup = fmt_speedup(b_us, a_us)
116
+
117
+ # ---- 三段结论 ----
118
+ if speedup is None:
119
+ conclusion = "UNRESOLVED:缺少可比的 kernel_avg_us(或标记为 partial/dry_run)"
120
+ action = "补齐性能数据或更换计时口径后重比"
121
+ elif speedup > 1 + args.noise_band:
122
+ conclusion = (
123
+ f"提升:speedup={speedup:.3f}x(>{1 + args.noise_band:.2f} 超出噪声带)"
124
+ )
125
+ action = "KEEP:该轮保留,进入下一轮单变量迭代"
126
+ elif speedup < 1 - args.noise_band:
127
+ conclusion = f"劣化:speedup={speedup:.3f}x(<{1 - args.noise_band:.2f})"
128
+ action = "REVERT:回滚本轮改动;回滚后须复跑确认回到基线水平再进入下一轮"
129
+ else:
130
+ conclusion = (
131
+ f"噪声带内({speedup:.3f}x,±{args.noise_band * 100:.0f}% 内):"
132
+ "不判收益也不判劣化"
133
+ )
134
+ # F9:repeat 是 HARD key——翻倍复测必须「以新 repeat 同步重采基线与优化侧、
135
+ # 重建快照(新目录)后再比」,直接翻倍 after 会被口径硬校验 exit 2 拒绝
136
+ action = (
137
+ "增加采样轮次(repeat 翻倍)复测:以新 repeat **同步重采基线与优化侧**、"
138
+ "在新目录重建两侧快照后再比(repeat 属口径指纹,单侧翻倍会被 HARD 校验拒绝);"
139
+ "仍落带内按无稳定收益处置"
140
+ )
141
+
142
+ # 误差指纹对照(精度数字必须同量级)
143
+ err_lines = []
144
+ for k in ("mismatch", "max_abs_err", "max_rel_err"):
145
+ bv, av = base.get(k), after.get(k)
146
+ if bv is not None or av is not None:
147
+ err_lines.append(f"| {k} | {bv} | {av} |")
148
+
149
+ # ---- 抖动诊断段(F20:分布字段存在时输出;缺失时向后兼容不输出)----
150
+ jitter_lines = []
151
+ for k in ("kernel_p50_us", "kernel_p95_us", "kernel_max_us"):
152
+ bv, av = base.get(k), after.get(k)
153
+ if bv is not None or av is not None:
154
+ jitter_lines.append(f"| {k} | {bv} | {av} |")
155
+ jitter_notes = []
156
+ for side_name, snap in (("baseline", base), ("after", after)):
157
+ p50, p95, avg = (
158
+ snap.get("kernel_p50_us"),
159
+ snap.get("kernel_p95_us"),
160
+ snap.get("kernel_avg_us"),
161
+ )
162
+ if p50 is not None and p95 is not None and avg:
163
+ if p50 * 2 < p95:
164
+ jitter_notes.append(
165
+ f"{side_name}: p95({p95})> 2×p50({p50})——疑似双峰分布,"
166
+ "先查是否有两种稳态(如 DVFS 档位 / 缓存对齐边界)再下结论"
167
+ )
168
+ elif p95 / avg > 1.5:
169
+ jitter_notes.append(
170
+ f"{side_name}: p95 偏离均值 {p95 / avg:.2f}x——长尾显著,"
171
+ "单均值口径下噪声带结论的置信度不足"
172
+ )
173
+ b_p50, b_p95 = base.get("kernel_p50_us"), base.get("kernel_p95_us")
174
+ a_p50, a_p95 = after.get("kernel_p50_us"), after.get("kernel_p95_us")
175
+ if b_p50 and b_p95 and a_p50 and a_p95:
176
+ if (b_p95 / b_p50) < (a_p95 / a_p50) * 0.5:
177
+ jitter_notes.append(
178
+ "优化侧 p95/p50 比值显著大于基线(≥2x)——收益集中在快轮次、"
179
+ "长尾反而恶化时,geomean/avg 口径可能掩盖劣化"
180
+ )
181
+
182
+ md = [
183
+ f"# 前后对比:{base.get('op')} — {after.get('variant', 'optimized')}",
184
+ "",
185
+ f"- 生成时间:{datetime.datetime.now().isoformat(timespec='seconds')}",
186
+ f"- 口径:{base.get('soc')} / CANN {base.get('cann_version')} / device {base.get('device_id')}"
187
+ f" / timing={base.get('timing_method')} / warmup={base.get('warmup')} repeat={base.get('repeat')}",
188
+ f"- 精度:baseline={base.get('precision')} after={after.get('precision')}(工程现有测试口径)",
189
+ "",
190
+ "## 结论",
191
+ "",
192
+ f"**{conclusion}**",
193
+ "",
194
+ f"处置:{action}",
195
+ "",
196
+ "## 数据",
197
+ "",
198
+ "| 项 | baseline | after |",
199
+ "|---|---|---|",
200
+ f"| kernel_avg_us | {b_us} | {a_us} |",
201
+ f"| speedup | — | {speedup if speedup is None else round(speedup, 3)} |",
202
+ f"| status | {base.get('status')} | {after.get('status')} |",
203
+ *err_lines,
204
+ ]
205
+ if mech_warns:
206
+ md += [
207
+ "",
208
+ "## 机制变量(预期内变化,WARN 记录)",
209
+ "",
210
+ *[f"- {w}" for w in mech_warns],
211
+ ]
212
+ if jitter_lines:
213
+ md += [
214
+ "",
215
+ "## 抖动诊断(分布字段)",
216
+ "",
217
+ "| 项 | baseline | after |",
218
+ "|---|---|---|",
219
+ *jitter_lines,
220
+ ]
221
+ if jitter_notes:
222
+ md += ["", *[f"- ⚠ {n}" for n in jitter_notes]]
223
+ if base.get("verdict_file") or after.get("verdict_file"):
224
+ md += [
225
+ "",
226
+ "## 关联判定书",
227
+ "",
228
+ f"- baseline verdict: {base.get('verdict_file')}",
229
+ f"- after verdict: {after.get('verdict_file')}",
230
+ ]
231
+
232
+ out = Path(args.output)
233
+ out.parent.mkdir(parents=True, exist_ok=True)
234
+ out.write_text("\n".join(md) + "\n", encoding="utf-8")
235
+ print(f"结论: {conclusion}")
236
+ print(f"处置: {action}")
237
+ if mech_warns:
238
+ print("WARN 机制变量变化(预期内): " + "; ".join(mech_warns))
239
+ print(f"→ {out}")
240
+ return 0
241
+
242
+
243
+ if __name__ == "__main__":
244
+ sys.exit(main())
@@ -0,0 +1,274 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # ----------------------------------------------------------------------------------------------------------
4
+ # Copyright (c) 2026 Huawei Technologies Co., Ltd.
5
+ # This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6
+ # CANN Open Software License Agreement Version 2.0 (the "License").
7
+ # Please refer to the License for details. You may not use this file except in compliance with the License.
8
+ # THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9
+ # INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10
+ # See LICENSE in the root of the software repository for the full text of the License.
11
+ # ----------------------------------------------------------------------------------------------------------
12
+ """render_html.py — comparison_matrix.md → 静态 HTML 报告(REVIEW2 G16 / 反馈 P2-3)
13
+
14
+ 设计对齐:
15
+ - 20 case × 多轮的对比矩阵纯文本人读不动(REVIEW2 G16);同类工具已有
16
+ 8 页交互式 HTML——本仓定位**静态 HTML**(数据已在 JSON/矩阵,不追交互,
17
+ 反馈 P2-3 措施原文:「数据已在 JSON,不追交互式」)。
18
+ - 输入两种形态任选:
19
+ ① batch_pipeline 的矩阵目录(逐 case 的 comparison_md + snapshot json——
20
+ 矩阵结论从 snapshot json 重读,保数值真源)
21
+ ② 显式 --snapshots(若干 result.json + comparison.md 对)——手工形态。
22
+ - 输出单文件 HTML(内联 CSS,零外部依赖,可离线打开 / 邮件附件 / 归档)。
23
+
24
+ 包含区块:
25
+ - 汇总卡片:geomean / KEEP / REVERT / 噪声带 / MISSING 计数
26
+ - 逐 variant 热力表:case × speedup 着色(绿=KEEP、红=REVERT、灰=带内/缺)
27
+ - 逐 case 明细折叠块(comparison.md 原文内嵌,<details> 展开查看)
28
+
29
+ 用法:
30
+ python3 render_html.py --matrix-dir tuning_artifacts/gelu/matrix -o report.html
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ import argparse
36
+ import datetime
37
+ import html
38
+ import json
39
+ import math
40
+ import re
41
+ from pathlib import Path
42
+
43
+
44
+ def load_matrix(matrix_dir: Path) -> dict:
45
+ """从矩阵目录重建逐 case 数据:{case: {variant: snapshot dict}} + 对比 md。"""
46
+ cases: dict[str, dict] = {}
47
+ comps: dict[tuple[str, str], str] = {}
48
+ for case_dir in sorted(p for p in matrix_dir.iterdir() if p.is_dir()):
49
+ cid = case_dir.name
50
+ for snap in case_dir.glob("*.json"):
51
+ data = json.loads(snap.read_text(encoding="utf-8"))
52
+ variant = data.get("variant", snap.stem)
53
+ cases.setdefault(cid, {})[variant] = data
54
+ for comp in case_dir.glob("*_comparison.md"):
55
+ variant = comp.name.replace("_comparison.md", "")
56
+ comps[(cid, variant)] = comp.read_text(encoding="utf-8")
57
+ return {"cases": cases, "comparisons": comps}
58
+
59
+
60
+ def conclusion_of(comp_md: str | None, speedup: float | None) -> str:
61
+ if comp_md:
62
+ if "提升" in comp_md and speedup and speedup > 1.05:
63
+ return "KEEP"
64
+ if "劣化" in comp_md:
65
+ return "REVERT"
66
+ if "噪声带内" in comp_md:
67
+ return "NOISE"
68
+ if "拒绝" in comp_md or "口径" in comp_md.split("## 结论")[0][:50]:
69
+ pass
70
+ if speedup is None:
71
+ return "MISSING"
72
+ return "NOISE"
73
+
74
+
75
+ def speedup_color(sp: float | None) -> str:
76
+ if sp is None:
77
+ return "#9e9e9e" # 灰:无数据
78
+ if sp >= 1.05:
79
+ # 绿色渐变:越深收益越大(1.05 → 浅,≥1.5 → 深)
80
+ t = min((sp - 1.05) / 0.45, 1.0)
81
+ return f"rgba(46, 125, 50, {0.25 + 0.55 * t})"
82
+ if sp <= 0.95:
83
+ # 红色渐变:越深劣化越大
84
+ t = min((0.95 - sp) / 0.25, 1.0)
85
+ return f"rgba(198, 40, 40, {0.25 + 0.55 * t})"
86
+ return "#e0e0e0" # 带内:浅灰
87
+
88
+
89
+ def md_to_html(md_text: str) -> str:
90
+ """comparison.md 的极简渲染:表原样 + 代码块 + 段落(不引 markdown 库,
91
+ 保持零依赖;已 html 转义后套结构)。"""
92
+ out = []
93
+ in_code = False
94
+ for line in md_text.splitlines():
95
+ e = html.escape(line)
96
+ if line.strip().startswith("```"):
97
+ out.append("</pre>" if in_code else "<pre>")
98
+ in_code = not in_code
99
+ elif in_code:
100
+ out.append(e)
101
+ elif line.startswith("|"):
102
+ cells = [c.strip() for c in line.strip("|").split("|")]
103
+ if set("".join(cells)) <= set("-: "):
104
+ continue # 分隔行
105
+ out.append(
106
+ "<tr>" + "".join(f"<td>{html.escape(c)}</td>" for c in cells) + "</tr>"
107
+ )
108
+ elif line.startswith("#"):
109
+ lvl = len(line) - len(line.lstrip("#"))
110
+ out.append(f"<h{min(lvl + 2, 6)}>{e.lstrip('# ')}</h{min(lvl + 2, 6)}>")
111
+ else:
112
+ out.append(f"<p>{e}</p>" if line.strip() else "")
113
+ body = "\n".join(out)
114
+ # 把首个 <tr> 前插表头结构(简化:全部 td)
115
+ return re.sub(r"((?:<tr>.*?</tr>\n?)+)", r"<table>\1</table>", body)
116
+
117
+
118
+ def render(data: dict, title: str) -> str:
119
+ cases = data["cases"]
120
+ comps = data["comparisons"]
121
+ variants = sorted(
122
+ {v for c in cases.values() for v in c}, key=lambda s: (s != "baseline", s)
123
+ )
124
+ case_ids = sorted(cases)
125
+ rows_speedup, statuses = {}, {}
126
+ for cid in case_ids:
127
+ base = cases[cid].get("baseline")
128
+ for v, snap in cases[cid].items():
129
+ if v == "baseline" or not base:
130
+ continue
131
+ b, a = base.get("kernel_avg_us"), snap.get("kernel_avg_us")
132
+ sp = (b / a) if (b and a) else None
133
+ rows_speedup[(cid, v)] = sp
134
+ statuses[(cid, v)] = conclusion_of(comps.get((cid, v)), sp)
135
+
136
+ # 汇总
137
+ per_variant: dict[str, dict] = {}
138
+ for v in variants:
139
+ if v == "baseline":
140
+ continue
141
+ sps = [sp for (cid, vv), sp in rows_speedup.items() if vv == v and sp]
142
+ sts = [st for (cid, vv), st in statuses.items() if vv == v]
143
+ per_variant[v] = {
144
+ "geomean": math.exp(sum(math.log(s) for s in sps) / len(sps))
145
+ if sps
146
+ else None,
147
+ "keep": sts.count("KEEP"),
148
+ "revert": sts.count("REVERT"),
149
+ "noise": sts.count("NOISE"),
150
+ "missing": sts.count("MISSING"),
151
+ "n": len(sts),
152
+ }
153
+
154
+ css = """
155
+ body { font-family: -apple-system, 'Segoe UI', 'PingFang SC', sans-serif; margin: 2rem auto;
156
+ max-width: 1100px; color: #212121; background: #fafafa; }
157
+ h1 { border-bottom: 2px solid #2e7d32; padding-bottom: .4rem; }
158
+ .cards { display: flex; gap: 1rem; flex-wrap: wrap; margin: 1rem 0 2rem; }
159
+ .card { background: #fff; border: 1px solid #e0e0e0; border-radius: 8px;
160
+ padding: .8rem 1.2rem; min-width: 130px; box-shadow: 0 1px 2px rgba(0,0,0,.06); }
161
+ .card .num { font-size: 1.6rem; font-weight: 600; }
162
+ .card .lbl { color: #757575; font-size: .8rem; }
163
+ table { border-collapse: collapse; background: #fff; margin: 1rem 0; width: 100%; }
164
+ th, td { border: 1px solid #e0e0e0; padding: .35rem .6rem; font-size: .85rem; text-align: center; }
165
+ th { background: #f5f5f5; }
166
+ td.sp { font-weight: 600; }
167
+ details { margin: .4rem 0; }
168
+ summary { cursor: pointer; color: #1565c0; font-size: .9rem; }
169
+ pre { background: #f5f5f5; padding: .6rem; border-radius: 4px; overflow-x: auto;
170
+ font-size: .8rem; }
171
+ .meta { color: #757575; font-size: .85rem; }
172
+ """
173
+
174
+ parts = [
175
+ "<!DOCTYPE html><html><head><meta charset='utf-8'>",
176
+ f"<title>{html.escape(title)}</title><style>{css}</style></head><body>",
177
+ f"<h1>{html.escape(title)}</h1>",
178
+ f"<p class='meta'>render_html.py 生成于 {datetime.datetime.now().isoformat(timespec='seconds')}"
179
+ f"(静态报告,数据真源为矩阵目录 snapshot JSON)</p>",
180
+ ]
181
+
182
+ for v, s in per_variant.items():
183
+ gm = f"{s['geomean']:.3f}x" if s["geomean"] else "—"
184
+ parts.append(
185
+ f"<div class='card'><div class='num'>{gm}</div><div class='lbl'>{html.escape(v)} geomean"
186
+ f"({s['n']} case)</div></div>"
187
+ f"<div class='card'><div class='num' style='color:#2e7d32'>{s['keep']}</div>"
188
+ f"<div class='lbl'>KEEP</div></div>"
189
+ f"<div class='card'><div class='num' style='color:#c62828'>{s['revert']}</div>"
190
+ f"<div class='lbl'>REVERT</div></div>"
191
+ f"<div class='card'><div class='num' style='color:#9e9e9e'>{s['noise']}</div>"
192
+ f"<div class='lbl'>噪声带内</div></div>"
193
+ f"<div class='card'><div class='num'>{s['missing']}</div>"
194
+ f"<div class='lbl'>MISSING</div></div>"
195
+ )
196
+
197
+ # 热力表
198
+ parts.append(
199
+ "<h2>逐 case 热力表</h2><table><tr><th>Case</th><th>Shape</th><th>dtype</th>"
200
+ + "".join(f"<th>{html.escape(v)}</th>" for v in variants if v != "baseline")
201
+ + "</tr>"
202
+ )
203
+ for cid in case_ids:
204
+ base = cases[cid].get("baseline", {})
205
+ parts.append(
206
+ f"<tr><td>{html.escape(cid)}</td>"
207
+ f"<td>{html.escape(str(base.get('shape', '')))}</td>"
208
+ f"<td>{html.escape(str(base.get('dtype', '')))}</td>"
209
+ )
210
+ for v in variants:
211
+ if v == "baseline":
212
+ continue
213
+ sp = rows_speedup.get((cid, v))
214
+ st = statuses.get((cid, v), "MISSING")
215
+ color = speedup_color(sp)
216
+ txt = f"{sp:.3f}x" if sp else "—"
217
+ parts.append(
218
+ f"<td class='sp' style='background:{color}' title='{html.escape(st)}'>"
219
+ f"{txt}</td>"
220
+ )
221
+ parts.append("</tr>")
222
+ parts.append("</table>")
223
+ parts.append(
224
+ "<p class='meta'>着色:绿=KEEP(越深收益越大);红=REVERT;"
225
+ "浅灰=噪声带内;深灰=无数据。悬停看结论。</p>"
226
+ )
227
+
228
+ # 逐 case 明细折叠
229
+ parts.append("<h2>逐 case 对比明细</h2>")
230
+ for (cid, v), md in sorted(comps.items()):
231
+ parts.append(
232
+ f"<details><summary>{html.escape(cid)} × {html.escape(v)}</summary>"
233
+ + md_to_html(md)
234
+ + "</details>"
235
+ )
236
+
237
+ parts.append("</body></html>")
238
+ return "\n".join(parts)
239
+
240
+
241
+ def main() -> int:
242
+ ap = argparse.ArgumentParser(
243
+ description="对比矩阵 → 静态 HTML 报告(零依赖单文件)"
244
+ )
245
+ ap.add_argument("--matrix-dir", required=True, help="batch_pipeline 的矩阵输出目录")
246
+ ap.add_argument("--title", default=None, help="报告标题(缺省取矩阵目录名)")
247
+ ap.add_argument("-o", "--output", required=True, help="HTML 输出路径")
248
+ args = ap.parse_args()
249
+
250
+ matrix_dir = Path(args.matrix_dir)
251
+ if not matrix_dir.is_dir():
252
+ print(f"ERROR: {matrix_dir} 不是目录", file=sys.stderr)
253
+ return 1
254
+ data = load_matrix(matrix_dir)
255
+ if not data["cases"]:
256
+ print("ERROR: 矩阵目录无 case 子目录(先跑 batch_pipeline)", file=sys.stderr)
257
+ return 1
258
+
259
+ title = (
260
+ args.title
261
+ or f"OpsTune 调优对比报告 — {matrix_dir.parent.name}/{matrix_dir.name}"
262
+ )
263
+ out = Path(args.output)
264
+ out.parent.mkdir(parents=True, exist_ok=True)
265
+ out.write_text(render(data, title), encoding="utf-8")
266
+ n = sum(len(v) - (1 if "baseline" in v else 0) for v in data["cases"].values())
267
+ print(f"html → {out}({len(data['cases'])} case × {n} 对比,静态单文件零依赖)")
268
+ return 0
269
+
270
+
271
+ if __name__ == "__main__":
272
+ import sys
273
+
274
+ sys.exit(main())