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.
- package/NOTICE.md +14 -0
- package/README.md +39 -2
- package/cordis.patch.yml +20 -0
- package/lib/client.js +4253 -0
- package/lib/host.js +2518 -0
- package/package.json +53 -4
- package/skills/tool-ops-tune/INSTALL.md +52 -0
- package/skills/tool-ops-tune/README.md +167 -0
- package/skills/tool-ops-tune/SKILL.md +93 -0
- package/skills/tool-ops-tune/agent/AGENTS.md +195 -0
- package/skills/tool-ops-tune/agent/agents/perf-implementer.md +68 -0
- package/skills/tool-ops-tune/agent/agents/perf-reviewer.md +86 -0
- package/skills/tool-ops-tune/agent/workflows/task-prompts.md +362 -0
- package/skills/tool-ops-tune/agent/workflows/troubleshooting.md +43 -0
- package/skills/tool-ops-tune/docs/figures/opstune-architecture.png +0 -0
- package/skills/tool-ops-tune/docs/figures/opstune-architecture.svg +253 -0
- package/skills/tool-ops-tune/docs/ops-tune-design.md +404 -0
- package/skills/tool-ops-tune/docs//347/224/250/344/276/213/346/236/204/345/273/272.md +714 -0
- package/skills/tool-ops-tune/docs//347/237/245/350/257/206/345/272/223/350/247/204/350/214/203.md +103 -0
- package/skills/tool-ops-tune/evals/evals.json +242 -0
- package/skills/tool-ops-tune/ops-collect/SKILL.md +146 -0
- package/skills/tool-ops-tune/ops-collect/references/tool-routing.md +86 -0
- package/skills/tool-ops-tune/ops-collect/scripts/collect_board.sh +444 -0
- package/skills/tool-ops-tune/ops-collect/scripts/collect_cases.py +368 -0
- package/skills/tool-ops-tune/ops-collect/scripts/collect_sim.sh +134 -0
- package/skills/tool-ops-tune/ops-collect/scripts/csv_to_db.py +314 -0
- package/skills/tool-ops-tune/ops-collect/scripts/db_bench.py +545 -0
- package/skills/tool-ops-tune/ops-collect/scripts/facts_bridge.py +1063 -0
- package/skills/tool-ops-tune/ops-collect/scripts/isolation_test.py +454 -0
- package/skills/tool-ops-tune/ops-collect/scripts/jsonl_to_cases.py +163 -0
- package/skills/tool-ops-tune/ops-family/SKILL.md +76 -0
- package/skills/tool-ops-tune/ops-family/references/broadcast/advanced_tiling.md +92 -0
- package/skills/tool-ops-tune/ops-family/references/broadcast/broadcast_design.md +160 -0
- package/skills/tool-ops-tune/ops-family/references/broadcast/code/broadcast_add_kernel.cpp +126 -0
- package/skills/tool-ops-tune/ops-family/references/broadcast/code/broadcast_add_tiling.cpp +144 -0
- package/skills/tool-ops-tune/ops-family/references/broadcast/code/broadcast_tiling_common.h +401 -0
- package/skills/tool-ops-tune/ops-family/references/broadcast/code/onedim_add_kernel.cpp +107 -0
- package/skills/tool-ops-tune/ops-family/references/broadcast/code/onedim_add_tiling.cpp +64 -0
- package/skills/tool-ops-tune/ops-family/references/broadcast/datacopypad_design.md +46 -0
- package/skills/tool-ops-tune/ops-family/references/broadcast/guide.md +14 -0
- package/skills/tool-ops-tune/ops-family/references/broadcast/nddma_design.md +53 -0
- package/skills/tool-ops-tune/ops-family/references/broadcast/onedim_design.md +114 -0
- package/skills/tool-ops-tune/ops-family/references/broadcast/ub_broadcast_design.md +67 -0
- package/skills/tool-ops-tune/ops-family/references/common/datacopy_optimization_design.md +351 -0
- package/skills/tool-ops-tune/ops-family/references/common/index.md +9 -0
- package/skills/tool-ops-tune/ops-family/references/common/tail_block_design.md +268 -0
- package/skills/tool-ops-tune/ops-family/references/common/timing_harness/index.md +38 -0
- package/skills/tool-ops-tune/ops-family/references/common/timing_harness/timing_harness_aclrt_event.cpp +199 -0
- package/skills/tool-ops-tune/ops-family/references/common/timing_harness/timing_harness_torch_event.py +130 -0
- package/skills/tool-ops-tune/ops-family/references/common/ub_resident_design.md +342 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/guide.md +69 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/index.md +4 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_base.template +81 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_big_dim.template +217 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_cut_one_axis.template +336 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_cut_two_axis.template +781 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_n_last.template +340 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_small_shape.template +321 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_tensor_move.template +102 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_transdata_5hd.template +480 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_transdata_5hd_021axis.template +533 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/templates/dav3510/transpose_with_gather.template +688 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/templates/transpose_tiling_data.template +192 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/transpose_big_dim.md +220 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/transpose_cut_one_axis.md +175 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/transpose_cut_two_axis.md +177 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/transpose_fusion_design.md +398 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/transpose_gather.md +191 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/transpose_n_last.md +276 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/transpose_small_shape.md +315 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/transpose_tensor_move.md +203 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/transpose_tiling_data.md +176 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/transpose_vconv_021.md +206 -0
- package/skills/tool-ops-tune/ops-family/references/conversion/transpose_vconv_5hd.md +179 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/double_buffer_design.md +288 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/guide.md +46 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/index.md +10 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/CMakeLists.txt +131 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/README.md +55 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_extension/add_custom_torch.cpp +80 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_extension/ops.h +23 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_extension/register.cpp +30 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_host/add_custom.asc +149 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_host/data_utils.h +97 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_kernel/add_custom_kernel.asc +136 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/op_kernel/add_custom_tiling.h +31 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/run.sh +86 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/scripts/gen_data.py +70 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/scripts/golden.py +34 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/scripts/test_torch.py +99 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/templates/add_custom/scripts/verify_result.py +56 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/templates/usage_guide.md +61 -0
- package/skills/tool-ops-tune/ops-family/references/elementwise/vector_efficiency_design.md +203 -0
- package/skills/tool-ops-tune/ops-family/references/fa/fa_mixed_kernel_design.md +174 -0
- package/skills/tool-ops-tune/ops-family/references/fa/fa_roofline_design.md +141 -0
- package/skills/tool-ops-tune/ops-family/references/fa/guide.md +87 -0
- package/skills/tool-ops-tune/ops-family/references/knowledge_gaps.json +56 -0
- package/skills/tool-ops-tune/ops-family/references/matmul/best-practices-guide.md +14 -0
- package/skills/tool-ops-tune/ops-family/references/matmul/fullload_design.md +344 -0
- package/skills/tool-ops-tune/ops-family/references/matmul/guide.md +52 -0
- package/skills/tool-ops-tune/ops-family/references/matmul/mte2_preload_design.md +320 -0
- package/skills/tool-ops-tune/ops-family/references/matmul/pingpong_design.md +254 -0
- package/skills/tool-ops-tune/ops-family/references/matmul/scale_coalescing_design.md +282 -0
- package/skills/tool-ops-tune/ops-family/references/matmul/streamk_design.md +374 -0
- package/skills/tool-ops-tune/ops-family/references/matmul/swat_design.md +423 -0
- package/skills/tool-ops-tune/ops-family/references/matmul/templates/matmul_basic_blaze.template +165 -0
- package/skills/tool-ops-tune/ops-family/references/matmul/templates/usage_guide.md +29 -0
- package/skills/tool-ops-tune/ops-family/references/mc2/best-practices-guide.md +43 -0
- package/skills/tool-ops-tune/ops-family/references/mc2/guide.md +42 -0
- package/skills/tool-ops-tune/ops-family/references/mc2/local_matmul_design.md +130 -0
- package/skills/tool-ops-tune/ops-family/references/mc2/pipeline_balancing_design.md +502 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/guide.md +178 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/kernel_utils.template +75 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ar_full_load.template +429 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ar_recompute.template +485 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ar_small_r.template +348 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ara_full_load.template +574 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ara_online.template +324 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_ara_recompute.template +436 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/dav310/softmax_v2_base.template +950 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/fused_attention_online_softmax_design.md +428 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ar_full_load.md +137 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ar_recompute.md +135 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ar_small_r.md +122 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ara_full_load.md +144 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ara_online.md +185 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_ara_recompute.md +163 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_tiling.template +460 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/softmax_v2_tiling_data.template +278 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/state_resident_design.md +337 -0
- package/skills/tool-ops-tune/ops-family/references/reduce/templates/usage_guide.md +253 -0
- package/skills/tool-ops-tune/ops-family/references/scalar/coding_principles.md +476 -0
- package/skills/tool-ops-tune/ops-family/references/scalar/conv_scalar.md +163 -0
- package/skills/tool-ops-tune/ops-family/references/scalar/guide.md +30 -0
- package/skills/tool-ops-tune/ops-family/references/simt/guide.md +91 -0
- package/skills/tool-ops-tune/ops-family/references/simt/optimization-guide.md +110 -0
- package/skills/tool-ops-tune/ops-family/references/sort/guide.md +83 -0
- package/skills/tool-ops-tune/ops-family/references/sort/radix_sort.md +354 -0
- package/skills/tool-ops-tune/ops-family/scripts/family_asset_audit.py +68 -0
- package/skills/tool-ops-tune/ops-family/scripts/tiling_candidates.py +419 -0
- package/skills/tool-ops-tune/ops-mechanism/SKILL.md +23 -0
- package/skills/tool-ops-tune/ops-mechanism/cases/ascendc/case_groupedmatmul_pipeline.md +23 -0
- package/skills/tool-ops-tune/ops-mechanism/cases/ascendc/case_triton_atomic_revert.md +19 -0
- package/skills/tool-ops-tune/ops-mechanism/cases/mc2/case_mc2_fusion.md +19 -0
- package/skills/tool-ops-tune/ops-mechanism/references/optimize/api-usage.md +70 -0
- package/skills/tool-ops-tune/ops-mechanism/references/optimize/ascendc-regbase-vf.md +45 -0
- package/skills/tool-ops-tune/ops-mechanism/references/optimize/ascendc-tiling.md +51 -0
- package/skills/tool-ops-tune/ops-mechanism/references/optimize/data-copy.md +67 -0
- package/skills/tool-ops-tune/ops-mechanism/references/optimize/index.md +45 -0
- package/skills/tool-ops-tune/ops-mechanism/references/optimize/mc2-balancing.md +77 -0
- package/skills/tool-ops-tune/ops-mechanism/references/optimize/mc2.md +103 -0
- package/skills/tool-ops-tune/ops-mechanism/references/optimize/memory-hierarchy.md +40 -0
- package/skills/tool-ops-tune/ops-mechanism/references/optimize/pipeline.md +95 -0
- package/skills/tool-ops-tune/ops-mechanism/scripts/case_backfill.py +170 -0
- package/skills/tool-ops-tune/ops-mechanism/scripts/log_gap.py +145 -0
- package/skills/tool-ops-tune/ops-mechanism/scripts/mc2_search.py +367 -0
- package/skills/tool-ops-tune/ops-report/SKILL.md +77 -0
- package/skills/tool-ops-tune/ops-verdict/evals/run_evals.py +837 -0
- package/skills/tool-ops-tune/ops-verdict/evals/smoke/install-hooks.sh +65 -0
- package/skills/tool-ops-tune/ops-verdict/evals/smoke/run_smoke.sh +405 -0
- package/skills/tool-ops-tune/ops-verdict/fixtures/core_tail_a5.csv +25 -0
- package/skills/tool-ops-tune/ops-verdict/fixtures/few_cores_a5.csv +5 -0
- package/skills/tool-ops-tune/ops-verdict/fixtures/mte2_bound_a2.csv +17 -0
- package/skills/tool-ops-tune/ops-verdict/fixtures/multi_kernel_mix_a5.csv +7 -0
- package/skills/tool-ops-tune/ops-verdict/fixtures/no_bound_a5.csv +9 -0
- package/skills/tool-ops-tune/ops-verdict/fixtures/serial_no_overlap_a2.csv +9 -0
- package/skills/tool-ops-tune/ops-verdict/fixtures/vec_bound_a5.csv +9 -0
- package/skills/tool-ops-tune/ops-verdict/references/io-contracts.md +94 -0
- package/skills/tool-ops-tune/ops-verdict/references/rule-schema.md +130 -0
- package/skills/tool-ops-tune/ops-verdict/references/scope-schema.md +61 -0
- package/skills/tool-ops-tune/ops-verdict/references/soc-spec.yaml +140 -0
- package/skills/tool-ops-tune/ops-verdict/rules/L0-instruction.yaml +67 -0
- package/skills/tool-ops-tune/ops-verdict/rules/L1-single-core.yaml +316 -0
- package/skills/tool-ops-tune/ops-verdict/rules/L2-inter-core.yaml +173 -0
- package/skills/tool-ops-tune/ops-verdict/rules/L3-inter-card.yaml +156 -0
- package/skills/tool-ops-tune/ops-verdict/scripts/batch_pipeline.py +444 -0
- package/skills/tool-ops-tune/ops-verdict/scripts/calibrate.py +249 -0
- package/skills/tool-ops-tune/ops-verdict/scripts/delta_compare.py +244 -0
- package/skills/tool-ops-tune/ops-verdict/scripts/render_html.py +274 -0
- package/skills/tool-ops-tune/ops-verdict/scripts/snapshot.py +200 -0
- package/skills/tool-ops-tune/ops-verdict/scripts/trace_bubble.py +331 -0
- package/skills/tool-ops-tune/ops-verdict/scripts/verdict.py +1072 -0
- package/skills/tool-ops-tune/scripts/diff_audit.py +281 -0
|
@@ -0,0 +1,454 @@
|
|
|
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
|
+
"""isolation_test.py — MC² 隔离测试编排(T_comm / T_compute 采集 → R 值)
|
|
13
|
+
|
|
14
|
+
设计对齐(docs/算子调优AGENT设计文档.md §4.3 数据集模型):
|
|
15
|
+
- MC² 隔离测试方法论此前长期停留在文档形态(复制源码、注释 process、tileCnt=1、
|
|
16
|
+
多 rank 采集、算 R 值全靠人工执行)—— 本脚本把它变成可重复的实验编排。
|
|
17
|
+
- 纪律继承:隔离测试在源码副本进行(不碰用户原目录);多 rank 并行采集;
|
|
18
|
+
循环 ≥10 次前 5 warm-up;跨 rank 取 max(木桶效应)。
|
|
19
|
+
- 输出直接可喂 verdict.py:--scalar t_comm_us=...,t_compute_us=...
|
|
20
|
+
|
|
21
|
+
用法(典型,宿主程序须支持 COMM_ONLY/COMPUTE_ONLY 环境变量开关或源码注释由 Agent 完成):
|
|
22
|
+
python3 isolation_test.py --exe ./build/mc2_demo --args "4096 4096 4096 4 perf 1" \\
|
|
23
|
+
--prof-dir ./prof/iso --output iso_result.json
|
|
24
|
+
|
|
25
|
+
输出 iso_result.json:
|
|
26
|
+
{ t_comm_us, t_compute_us, r_value, bound_hint, rank_detail: {...}, steady_rule: "10轮前5warmup后5均值,跨rank取max" }
|
|
27
|
+
|
|
28
|
+
R 值档位真源:ops-verdict 规则包 L3-inter-card.yaml 的 r_value 叶子条件(脚本启动时解析,
|
|
29
|
+
解析失败即报错退出——fail loud,保证规则演进时脚本不会静默回退到旧常量)。
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import argparse
|
|
35
|
+
import csv
|
|
36
|
+
import json
|
|
37
|
+
import os
|
|
38
|
+
import subprocess
|
|
39
|
+
import sys
|
|
40
|
+
from pathlib import Path
|
|
41
|
+
|
|
42
|
+
WARMUP = 5 # 前 5 次 warm-up(L2 预热、DVFS 爬升)
|
|
43
|
+
LOOPS = 10 # 循环至少 10 次
|
|
44
|
+
# MTE2 占比候选列(与 L1 词汇表 mte2_ratio 同源,三代列名兼容)
|
|
45
|
+
MTE2_RATIO_COLUMNS = [
|
|
46
|
+
"aic_mte2_ratio",
|
|
47
|
+
"aiv_mte2_ratio",
|
|
48
|
+
"ai_mte2_ratio",
|
|
49
|
+
"Mte2Utilization(%)",
|
|
50
|
+
"mte2_ratio",
|
|
51
|
+
"MTE2(%)",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
# L3 规则包默认路径(相对本脚本定位,仓内布局免传参;--rules 可覆写)
|
|
55
|
+
L3_RULES_FILE = "L3-inter-card" + ".yaml" # MC² 卡间判定规则包
|
|
56
|
+
DEFAULT_L3_RULES = (
|
|
57
|
+
Path(__file__).resolve().parents[2] / "ops-verdict" / "rules" / L3_RULES_FILE
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def r_bands_from_rules(rules_path: str) -> list[tuple]:
|
|
62
|
+
"""从 L3 规则包 YAML 派生 R 值档位表(规则 YAML 是唯一真源)。
|
|
63
|
+
|
|
64
|
+
解析每条以 r_value 为唯一 metric 叶子条件、区间形状完整的规则,产出
|
|
65
|
+
[(lo, hi, rule_id, name), ...] 按 hi 升序(lo=None 表示无下界)。
|
|
66
|
+
解析不出任何档位 → 直接抛错退出(fail loud)。
|
|
67
|
+
"""
|
|
68
|
+
import yaml
|
|
69
|
+
|
|
70
|
+
pack = yaml.safe_load(Path(rules_path).read_text(encoding="utf-8"))
|
|
71
|
+
bands = []
|
|
72
|
+
for rule in pack.get("rules", []):
|
|
73
|
+
leaves = _collect_metric_leaves(rule.get("condition"), "r_value")
|
|
74
|
+
if not leaves:
|
|
75
|
+
continue
|
|
76
|
+
lo = hi = None
|
|
77
|
+
ok = True
|
|
78
|
+
for leaf in leaves:
|
|
79
|
+
op, val = leaf["op"], leaf["value"]
|
|
80
|
+
if op == "between" and isinstance(val, list) and len(val) == 2:
|
|
81
|
+
lo, hi = float(val[0]), float(val[1])
|
|
82
|
+
elif isinstance(val, (int, float)):
|
|
83
|
+
if op in ("gt", "gte") and lo is None:
|
|
84
|
+
lo = float(val)
|
|
85
|
+
elif op in ("lt", "lte") and hi is None:
|
|
86
|
+
hi = float(val)
|
|
87
|
+
else:
|
|
88
|
+
ok = False
|
|
89
|
+
break
|
|
90
|
+
else:
|
|
91
|
+
ok = False
|
|
92
|
+
break
|
|
93
|
+
if ok and (lo is not None or hi is not None):
|
|
94
|
+
# 单叶 gt(无上界,如 STRONG_COMPUTE R>2):hi 置 inf,作为兜底档
|
|
95
|
+
bands.append(
|
|
96
|
+
(
|
|
97
|
+
lo,
|
|
98
|
+
hi if hi is not None else float("inf"),
|
|
99
|
+
rule["id"],
|
|
100
|
+
rule.get("name", rule["id"]),
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
if not bands:
|
|
104
|
+
raise RuntimeError(
|
|
105
|
+
f"无法从 {rules_path} 解析出 r_value 档位(规则结构变化?fail loud 退出)"
|
|
106
|
+
)
|
|
107
|
+
return sorted(bands, key=lambda b: b[1])
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def classify_r(r: float, bands: list[tuple]) -> tuple[str, str]:
|
|
111
|
+
"""按派生档位分类 R 值:唯一命中档 = 唯一满足区间条件的档;多重命中取最紧。
|
|
112
|
+
|
|
113
|
+
边界对齐规则表语义:STRONG_COMM 为开区间上界(R<hi),其余档下界 gte /
|
|
114
|
+
上界 lte/between——r 恰落档界(R=0.5/0.9/1.1/2.0)时同时满足两档的
|
|
115
|
+
"r ≤ hi"宽松判定,须比较真实区间条件:开上界档要求 r < hi。
|
|
116
|
+
"""
|
|
117
|
+
# 第一轮:严格区间命中(lo ≤ r 且 r < hi 开上界 / r ≤ hi 闭上界)
|
|
118
|
+
strict = []
|
|
119
|
+
for lo, hi, bid, name in bands:
|
|
120
|
+
if (lo is None or r >= lo) and r < hi:
|
|
121
|
+
strict.append((lo, hi, bid, name))
|
|
122
|
+
elif (lo is None or r >= lo) and r == hi:
|
|
123
|
+
# r 恰等于闭上界档(between/lte)的 hi → 该档命中(如 BALANCED [0.9,1.1] 命中 R=1.1)
|
|
124
|
+
# 与开上界档(lt)区分:开上界在 r==hi 时不命中
|
|
125
|
+
strict.append((lo, hi, bid, name)) if _has_closed_upper(
|
|
126
|
+
bid, bands
|
|
127
|
+
) else None
|
|
128
|
+
if len(strict) == 1:
|
|
129
|
+
return strict[0][2], strict[0][3]
|
|
130
|
+
if len(strict) > 1:
|
|
131
|
+
# 多重命中取最紧上界档(区间重叠时的确定性规则;正常规则集不触发)
|
|
132
|
+
strict.sort(key=lambda b: b[1])
|
|
133
|
+
return strict[0][2], strict[0][3]
|
|
134
|
+
# 无严格命中(r 落在档界间隙或超出覆盖)→ 兜底最紧上界
|
|
135
|
+
for lo, hi, bid, name in bands:
|
|
136
|
+
if r <= hi:
|
|
137
|
+
return bid, name
|
|
138
|
+
return "UNRESOLVED", "超出规则档位覆盖范围(检查 L3 规则包区间连续性)"
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _has_closed_upper(bid: str, bands: list[tuple]) -> bool:
|
|
142
|
+
"""该档上界是否为闭(between/lte)。当前规则集:STRONG_COMM(lt)与
|
|
143
|
+
COMM_BOUND(lt 上界)为开,BALANCED/COMPUTE_BOUND 为闭。按 id 约定
|
|
144
|
+
强通信/通信档上界开、其余闭——由 r_bands_from_rules 的区间形状保证。"""
|
|
145
|
+
return bid not in ("L3_MC2_STRONG_COMM", "L3_MC2_COMM_BOUND")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _collect_metric_leaves(cond, metric: str) -> list[dict]:
|
|
149
|
+
"""递归收集条件树中引用指定 metric 的叶子。"""
|
|
150
|
+
out = []
|
|
151
|
+
if cond is None:
|
|
152
|
+
return out
|
|
153
|
+
if isinstance(cond, list):
|
|
154
|
+
for sub in cond:
|
|
155
|
+
out.extend(_collect_metric_leaves(sub, metric))
|
|
156
|
+
return out
|
|
157
|
+
if isinstance(cond, dict):
|
|
158
|
+
if "metric" in cond and cond["metric"] == metric:
|
|
159
|
+
out.append(cond)
|
|
160
|
+
for key in ("all", "any"):
|
|
161
|
+
if key in cond:
|
|
162
|
+
out.extend(_collect_metric_leaves(cond[key], metric))
|
|
163
|
+
return out
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def parse_task_durations(prof_dir: str) -> dict[str, list[float]]:
|
|
167
|
+
"""从 msprof 产出的 op_summary_*.csv 提取 Task Duration(us),按文件分组返回。
|
|
168
|
+
|
|
169
|
+
每个文件 ≈ 一个 rank 的独立 op_summary(msprof 每设备一份 PROF 目录)——
|
|
170
|
+
分组是「逐 rank 稳态均值再跨 rank 取 max」(木桶口径)的前提(检视 F19:
|
|
171
|
+
全部行统一求均值在数学上不等价,mean(所有行) ≤ max(各 rank 均值),
|
|
172
|
+
系统性低估 T → R 偏向「计算 bound」结论)。
|
|
173
|
+
返回 {文件名: [时长列表]};空文件不产生键。
|
|
174
|
+
"""
|
|
175
|
+
groups: dict[str, list[float]] = {}
|
|
176
|
+
for p in sorted(Path(prof_dir).rglob("op_summary_*.csv")):
|
|
177
|
+
vals = []
|
|
178
|
+
with open(p, encoding="utf-8-sig", errors="replace") as f:
|
|
179
|
+
for row in csv.DictReader(f):
|
|
180
|
+
v = row.get("Task Duration(us)") or row.get("Task Duration")
|
|
181
|
+
if v:
|
|
182
|
+
try:
|
|
183
|
+
vals.append(float(str(v).strip()))
|
|
184
|
+
except ValueError:
|
|
185
|
+
pass
|
|
186
|
+
if vals:
|
|
187
|
+
groups[str(p)] = vals
|
|
188
|
+
return groups
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def steady_max(grouped: dict[str, list[float]], warmup: int) -> float | None:
|
|
192
|
+
"""木桶口径稳态聚合(检视 F19):每 rank(文件组)内丢弃前 warmup 次取均值,
|
|
193
|
+
再跨 rank 取 max——所有 rank 经 BarrierAll/CrossCoreFlag 同步,整体性能由
|
|
194
|
+
最慢 rank 决定。"""
|
|
195
|
+
per_rank = []
|
|
196
|
+
for vals in grouped.values():
|
|
197
|
+
steady = vals[warmup:] if len(vals) > warmup else vals
|
|
198
|
+
if steady:
|
|
199
|
+
per_rank.append(sum(steady) / len(steady))
|
|
200
|
+
return max(per_rank) if per_rank else None
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def parse_pipe_ratio(prof_dir: str, columns: list[str]) -> dict[str, float]:
|
|
204
|
+
"""从 op_summary_*.csv 提取 pipe 占比列的逐 rank 稳态均值(F8:mte2_ratio_pure 等的生产者)。
|
|
205
|
+
|
|
206
|
+
返回 {文件名: 稳态均值};列不存在或全空 → 空 dict。
|
|
207
|
+
"""
|
|
208
|
+
groups: dict[str, float] = {}
|
|
209
|
+
for p in sorted(Path(prof_dir).rglob("op_summary_*.csv")):
|
|
210
|
+
vals = []
|
|
211
|
+
with open(p, encoding="utf-8-sig", errors="replace") as f:
|
|
212
|
+
for row in csv.DictReader(f):
|
|
213
|
+
for col in columns:
|
|
214
|
+
v = row.get(col)
|
|
215
|
+
if v and str(v).strip() not in ("", "N/A", "NA", "-"):
|
|
216
|
+
try:
|
|
217
|
+
fv = float(str(v).strip().replace("%", ""))
|
|
218
|
+
vals.append(
|
|
219
|
+
fv * 100 if 0 <= fv <= 1.5 else fv
|
|
220
|
+
) # ratio_to_pct 兼容
|
|
221
|
+
break
|
|
222
|
+
except ValueError:
|
|
223
|
+
pass
|
|
224
|
+
if vals:
|
|
225
|
+
groups[str(p)] = sum(vals) / len(vals)
|
|
226
|
+
return groups
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def parse_switch(spec: str) -> dict:
|
|
230
|
+
"""把 --comm-switch/--compute-switch 解析成环境变量注入 dict。
|
|
231
|
+
|
|
232
|
+
支持两种形式:
|
|
233
|
+
- 纯 KEY=VALUE 或 KEY=VALUE,K2=V2:注入这些变量(默认 MC2_MODE=COMM_ONLY 等)
|
|
234
|
+
- 裸字符串(无 =):视为提示文本,不注入(打印用)
|
|
235
|
+
"""
|
|
236
|
+
env = {}
|
|
237
|
+
for kv in spec.split(","):
|
|
238
|
+
kv = kv.strip()
|
|
239
|
+
if "=" in kv:
|
|
240
|
+
k, v = kv.split("=", 1)
|
|
241
|
+
env[k.strip()] = v.strip()
|
|
242
|
+
return env
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def run_msprof_task_based(
|
|
246
|
+
exe: str, exe_args: str, out: str, env_extra: dict
|
|
247
|
+
) -> dict[str, list[float]]:
|
|
248
|
+
"""msprof task-based 采集一轮(MC² 专用路径),返回按 op_summary 文件(≈按 rank)
|
|
249
|
+
分组的 Task Duration。
|
|
250
|
+
|
|
251
|
+
env 采用整体替换语义(subprocess.run(env=...) 不继承父进程环境),因此必须
|
|
252
|
+
以 os.environ 为底、env_extra 覆盖——否则 PATH/ASCEND_HOME_PATH/LD_LIBRARY_PATH
|
|
253
|
+
全部丢失,msprof 无法运行(检视 F2)。
|
|
254
|
+
"""
|
|
255
|
+
env = {**os.environ, **env_extra}
|
|
256
|
+
cmd = [
|
|
257
|
+
"msprof",
|
|
258
|
+
f"--output={out}",
|
|
259
|
+
"--ai-core=on",
|
|
260
|
+
"--aic-mode=task-based",
|
|
261
|
+
"--aic-metrics=PipeUtilization",
|
|
262
|
+
"--task-time=on",
|
|
263
|
+
"--ascendcl=on",
|
|
264
|
+
f"--application={exe} {exe_args}",
|
|
265
|
+
]
|
|
266
|
+
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=1800, env=env)
|
|
267
|
+
if proc.returncode != 0:
|
|
268
|
+
raise RuntimeError(f"msprof 失败: {proc.stderr[:400]}")
|
|
269
|
+
return parse_task_durations(out)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def main() -> int:
|
|
273
|
+
ap = argparse.ArgumentParser(description="MC² 隔离测试编排(T_comm/T_compute → R)")
|
|
274
|
+
ap.add_argument(
|
|
275
|
+
"--exe", required=True, help="待测可执行文件(须按 rank 数自行 fork)"
|
|
276
|
+
)
|
|
277
|
+
ap.add_argument(
|
|
278
|
+
"--args", default="", help='程序参数(如 "M K N rankNum perf tileCnt")'
|
|
279
|
+
)
|
|
280
|
+
ap.add_argument(
|
|
281
|
+
"--comm-switch",
|
|
282
|
+
default="MC2_MODE=COMM_ONLY",
|
|
283
|
+
help="纯通信开关(K=V 或 K1=V1,K2=V2 环境变量形式,注入采集子进程;"
|
|
284
|
+
"源码注释模式由 Agent 在副本完成后调用本脚本 --from-prof 收敛)",
|
|
285
|
+
)
|
|
286
|
+
ap.add_argument(
|
|
287
|
+
"--compute-switch",
|
|
288
|
+
default="MC2_MODE=COMPUTE_ONLY",
|
|
289
|
+
help="纯计算开关(同上形式,注入采集子进程)",
|
|
290
|
+
)
|
|
291
|
+
ap.add_argument(
|
|
292
|
+
"--switch-mode",
|
|
293
|
+
default="env",
|
|
294
|
+
choices=["env", "from-prof"],
|
|
295
|
+
help="env=程序自带模式开关;from-prof=已手工注释的两份 profiling 目录直接给路径",
|
|
296
|
+
)
|
|
297
|
+
ap.add_argument(
|
|
298
|
+
"--comm-prof", default=None, help="from-prof 模式:纯通信 profiling 目录"
|
|
299
|
+
)
|
|
300
|
+
ap.add_argument(
|
|
301
|
+
"--compute-prof", default=None, help="from-prof 模式:纯计算 profiling 目录"
|
|
302
|
+
)
|
|
303
|
+
ap.add_argument("--prof-dir", default="./prof/iso", help="env 模式采集落盘根目录")
|
|
304
|
+
ap.add_argument(
|
|
305
|
+
"--rules",
|
|
306
|
+
default=str(DEFAULT_L3_RULES),
|
|
307
|
+
help="L3 规则包 YAML(R 值档位唯一真源;默认仓内 L3-inter-card.yaml)",
|
|
308
|
+
)
|
|
309
|
+
ap.add_argument("--loops", type=int, default=LOOPS)
|
|
310
|
+
ap.add_argument("--warmup", type=int, default=WARMUP)
|
|
311
|
+
ap.add_argument("--output", required=True)
|
|
312
|
+
args = ap.parse_args()
|
|
313
|
+
|
|
314
|
+
if args.switch_mode == "from-prof":
|
|
315
|
+
comm_groups = parse_task_durations(args.comm_prof)
|
|
316
|
+
compute_groups = parse_task_durations(args.compute_prof)
|
|
317
|
+
if not comm_groups or not compute_groups:
|
|
318
|
+
print(
|
|
319
|
+
"ERROR: from-prof 目录中未找到 op_summary Task Duration 数据",
|
|
320
|
+
file=sys.stderr,
|
|
321
|
+
)
|
|
322
|
+
return 1
|
|
323
|
+
t_comm, t_compute = (
|
|
324
|
+
steady_max(comm_groups, args.warmup),
|
|
325
|
+
steady_max(compute_groups, args.warmup),
|
|
326
|
+
)
|
|
327
|
+
t_full, full_groups, mte2_ratio_full = None, {}, None
|
|
328
|
+
detail = {
|
|
329
|
+
"mode": "from-prof",
|
|
330
|
+
"comm_prof": args.comm_prof,
|
|
331
|
+
"compute_prof": args.compute_prof,
|
|
332
|
+
}
|
|
333
|
+
# F8:纯计算路径 pipe 占比(mte2_ratio_pure 的生产者——隔离计算 run 的
|
|
334
|
+
# aic_mte2_ratio 稳态均值,跨 rank 取 max,与 T 口径一致)
|
|
335
|
+
compute_pure_ratio = parse_pipe_ratio(args.compute_prof, MTE2_RATIO_COLUMNS)
|
|
336
|
+
mte2_ratio_pure = (
|
|
337
|
+
round(max(compute_pure_ratio.values()), 1) if compute_pure_ratio else None
|
|
338
|
+
)
|
|
339
|
+
else:
|
|
340
|
+
# env 开关模式:--comm-switch/--compute-switch 支持 K=V(或 K1=V1,K2=V2)形式,
|
|
341
|
+
# 解析出的变量真正注入子进程 env(消灭死参数,检视 F2/N2)
|
|
342
|
+
comm_env = parse_switch(args.comm_switch) or {"MC2_MODE": "COMM_ONLY"}
|
|
343
|
+
compute_env = parse_switch(args.compute_switch) or {"MC2_MODE": "COMPUTE_ONLY"}
|
|
344
|
+
comm_out = str(Path(args.prof_dir) / "comm")
|
|
345
|
+
compute_out = str(Path(args.prof_dir) / "compute")
|
|
346
|
+
print(f"[iso] 采集纯通信({args.comm_switch})…")
|
|
347
|
+
comm_groups = run_msprof_task_based(args.exe, args.args, comm_out, comm_env)
|
|
348
|
+
print(f"[iso] 采集纯计算({args.compute_switch})…")
|
|
349
|
+
compute_groups = run_msprof_task_based(
|
|
350
|
+
args.exe, args.args, compute_out, compute_env
|
|
351
|
+
)
|
|
352
|
+
print("[iso] 采集融合态(全量,F8:mte2_ratio_full / mask_ratio 生产者)…")
|
|
353
|
+
full_out = str(Path(args.prof_dir) / "full")
|
|
354
|
+
full_groups = run_msprof_task_based(args.exe, args.args, full_out, {})
|
|
355
|
+
t_comm, t_compute = (
|
|
356
|
+
steady_max(comm_groups, args.warmup),
|
|
357
|
+
steady_max(compute_groups, args.warmup),
|
|
358
|
+
)
|
|
359
|
+
t_full = steady_max(full_groups, args.warmup)
|
|
360
|
+
detail = {
|
|
361
|
+
"mode": "env",
|
|
362
|
+
"comm_ranks": len(comm_groups),
|
|
363
|
+
"compute_ranks": len(compute_groups),
|
|
364
|
+
"full_ranks": len(full_groups),
|
|
365
|
+
}
|
|
366
|
+
compute_pure_ratio = parse_pipe_ratio(compute_out, MTE2_RATIO_COLUMNS)
|
|
367
|
+
mte2_ratio_pure = (
|
|
368
|
+
round(max(compute_pure_ratio.values()), 1) if compute_pure_ratio else None
|
|
369
|
+
)
|
|
370
|
+
full_ratio = parse_pipe_ratio(full_out, MTE2_RATIO_COLUMNS)
|
|
371
|
+
mte2_ratio_full = round(max(full_ratio.values()), 1) if full_ratio else None
|
|
372
|
+
|
|
373
|
+
if not t_comm or not t_compute:
|
|
374
|
+
print("ERROR: 稳态数据为空(检查 op_summary 行数 ≥ loops)", file=sys.stderr)
|
|
375
|
+
return 1
|
|
376
|
+
|
|
377
|
+
# F8:rank_imbalance = max(rank 稳态均值)/mean(rank 稳态均值)(融合态,跨 rank 木桶离散度)
|
|
378
|
+
rank_imbalance = None
|
|
379
|
+
if t_full is not None and full_groups:
|
|
380
|
+
rank_means = []
|
|
381
|
+
for vals in full_groups.values():
|
|
382
|
+
steady = vals[args.warmup :] if len(vals) > args.warmup else vals
|
|
383
|
+
if steady:
|
|
384
|
+
rank_means.append(sum(steady) / len(steady))
|
|
385
|
+
if len(rank_means) >= 2 and sum(rank_means) > 0:
|
|
386
|
+
rank_imbalance = round(
|
|
387
|
+
max(rank_means) / (sum(rank_means) / len(rank_means)), 3
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
# F8:mask_ratio = (融合前耗时 − 融合后耗时) / min(T_comm, T_compute),上限 1.0
|
|
391
|
+
# 融合前耗时 = T_comm + T_compute(隔离态串行和);融合后 = t_full
|
|
392
|
+
mask_ratio = None
|
|
393
|
+
if t_full is not None and t_comm and t_compute:
|
|
394
|
+
denom = min(t_comm, t_compute)
|
|
395
|
+
if denom > 0:
|
|
396
|
+
mask_ratio = round(min((t_comm + t_compute - t_full) / denom, 1.0), 3)
|
|
397
|
+
|
|
398
|
+
r = t_compute / t_comm
|
|
399
|
+
# 档位从 L3 规则包 YAML 派生(规则是唯一真源;解析失败在 r_bands_from_rules 里 fail loud)
|
|
400
|
+
bound, bound_name = classify_r(r, r_bands_from_rules(args.rules))
|
|
401
|
+
|
|
402
|
+
# F8:产出直接拼成 verdict.py 的 --scalar 串(MC² 旗舰判定链端到端可执行)
|
|
403
|
+
scalar_parts = [f"t_comm_us={t_comm:.0f}", f"t_compute_us={t_compute:.0f}"]
|
|
404
|
+
if mte2_ratio_full is not None:
|
|
405
|
+
scalar_parts.append(f"mte2_ratio_full={mte2_ratio_full:.0f}")
|
|
406
|
+
if mte2_ratio_pure is not None:
|
|
407
|
+
scalar_parts.append(f"mte2_ratio_pure={mte2_ratio_pure:.0f}")
|
|
408
|
+
if rank_imbalance is not None:
|
|
409
|
+
scalar_parts.append(f"rank_imbalance={rank_imbalance}")
|
|
410
|
+
if mask_ratio is not None:
|
|
411
|
+
scalar_parts.append(f"mask_ratio={mask_ratio}")
|
|
412
|
+
scalar_str = ",".join(scalar_parts)
|
|
413
|
+
|
|
414
|
+
result = {
|
|
415
|
+
"schema": "opstune.isolation/1.0",
|
|
416
|
+
"t_comm_us": round(t_comm, 2),
|
|
417
|
+
"t_compute_us": round(t_compute, 2),
|
|
418
|
+
"t_full_us": round(t_full, 2) if t_full is not None else None,
|
|
419
|
+
"r_value": round(r, 3),
|
|
420
|
+
"bound_hint": bound,
|
|
421
|
+
"bound_name": bound_name,
|
|
422
|
+
"mte2_ratio_pure": mte2_ratio_pure,
|
|
423
|
+
"mte2_ratio_full": mte2_ratio_full,
|
|
424
|
+
"rank_imbalance": rank_imbalance,
|
|
425
|
+
"mask_ratio": mask_ratio,
|
|
426
|
+
"verdict_scalar": scalar_str,
|
|
427
|
+
"r_bands_source": str(Path(args.rules).name),
|
|
428
|
+
"steady_rule": (
|
|
429
|
+
f"{args.loops} 轮前 {args.warmup} warm-up 后逐 rank 均值,"
|
|
430
|
+
"跨 rank 取 max(木桶效应)"
|
|
431
|
+
),
|
|
432
|
+
"detail": detail,
|
|
433
|
+
"next": (
|
|
434
|
+
f"喂判定引擎:python3 ../ops-verdict/scripts/verdict.py "
|
|
435
|
+
f"--rules ../ops-verdict/rules/L3-inter-card.yaml,../ops-verdict/rules/L1-single-core.yaml "
|
|
436
|
+
f"--soc a5 --scalar {scalar_str} -o verdict_mc2.json"
|
|
437
|
+
),
|
|
438
|
+
}
|
|
439
|
+
out = Path(args.output)
|
|
440
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
441
|
+
out.write_text(
|
|
442
|
+
json.dumps(result, indent=2, ensure_ascii=False) + "\n", encoding="utf-8"
|
|
443
|
+
)
|
|
444
|
+
print(f"T_comm={t_comm:.1f}us T_compute={t_compute:.1f}us R={r:.3f} → {bound}")
|
|
445
|
+
if mte2_ratio_pure is not None or mte2_ratio_full is not None:
|
|
446
|
+
print(f"mte2_ratio: pure={mte2_ratio_pure} full={mte2_ratio_full}")
|
|
447
|
+
if rank_imbalance is not None:
|
|
448
|
+
print(f"rank_imbalance={rank_imbalance} mask_ratio={mask_ratio}")
|
|
449
|
+
print(f"→ {out}")
|
|
450
|
+
return 0
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
if __name__ == "__main__":
|
|
454
|
+
sys.exit(main())
|
|
@@ -0,0 +1,163 @@
|
|
|
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
|
+
"""jsonl_to_cases.py — JSONL 用例 → cases.csv 适配(REVIEW2 G11 / 反馈 P1-5①)
|
|
13
|
+
|
|
14
|
+
设计对齐:
|
|
15
|
+
- cases.csv 是本仓 A1 全量铁律的输入契约(io-contracts.md §1:case_id/shape/
|
|
16
|
+
dtype/attrs),但生态内存量算子工程用 JSONL 用例(torch-ops-profiler
|
|
17
|
+
规范:每行 {"inputs": [{name/type/dtype/shape|value}...]})——两格式不通,
|
|
18
|
+
存量工程进不了 Phase 0(REVIEW2 G11 定性)。
|
|
19
|
+
- 本脚本做单向适配:JSONL → cases.csv。转换规则全落脚本(判定/转换逻辑
|
|
20
|
+
不进 LLM),歧义处 fail loud 列入 skipped 而非猜测。
|
|
21
|
+
|
|
22
|
+
转换规则:
|
|
23
|
+
case_id :行号(c01 起)或 JSONL 行自带 case_id/name 字段(优先)
|
|
24
|
+
shape :首个 type=tensor 输入的 shape(多维 x 分隔);无 tensor 输入
|
|
25
|
+
→ 该行 skipped(无性能可比形态)
|
|
26
|
+
dtype :首个 tensor 输入的 dtype(float16→fp16 等短名归一)
|
|
27
|
+
attrs :type != tensor 的输入(attr 类)拼 K=V(value 为列表/布尔的
|
|
28
|
+
原样 JSON 序列化),保留原 name
|
|
29
|
+
|
|
30
|
+
用法:
|
|
31
|
+
python3 jsonl_to_cases.py --jsonl layer_norm_perf_cases.jsonl -o cases.csv
|
|
32
|
+
python3 jsonl_to_cases.py --jsonl x.jsonl -o cases.csv --max-cases 20 # A1 截断同款
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
import argparse
|
|
38
|
+
import csv
|
|
39
|
+
import json
|
|
40
|
+
import sys
|
|
41
|
+
from pathlib import Path
|
|
42
|
+
|
|
43
|
+
DTYPE_MAP = {
|
|
44
|
+
"float16": "fp16",
|
|
45
|
+
"float32": "fp32",
|
|
46
|
+
"bfloat16": "bf16",
|
|
47
|
+
"int8": "int8",
|
|
48
|
+
"int32": "int32",
|
|
49
|
+
"int64": "int64",
|
|
50
|
+
"float": "fp32",
|
|
51
|
+
"half": "fp16",
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def fmt_value(v) -> str:
|
|
56
|
+
if isinstance(v, bool):
|
|
57
|
+
return "true" if v else "false"
|
|
58
|
+
if isinstance(v, (list, tuple)):
|
|
59
|
+
return "x".join(str(x) for x in v)
|
|
60
|
+
return str(v)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def convert_row(idx: int, row: dict) -> tuple[dict | None, str | None]:
|
|
64
|
+
"""一行 JSONL → 一行 cases.csv(或 skip 原因)。"""
|
|
65
|
+
case_id = row.get("case_id") or row.get("name") or f"c{idx:02d}"
|
|
66
|
+
inputs = row.get("inputs")
|
|
67
|
+
if not isinstance(inputs, list) or not inputs:
|
|
68
|
+
return None, f"{case_id}: 无 inputs 数组"
|
|
69
|
+
tensors = [i for i in inputs if isinstance(i, dict) and i.get("type") == "tensor"]
|
|
70
|
+
if not tensors:
|
|
71
|
+
return None, f"{case_id}: 无 tensor 输入(无可比性能形态)"
|
|
72
|
+
t = tensors[0]
|
|
73
|
+
shape = t.get("shape")
|
|
74
|
+
if not isinstance(shape, (list, tuple)) or not shape:
|
|
75
|
+
return None, f"{case_id}: tensor 输入无 shape"
|
|
76
|
+
dtype = DTYPE_MAP.get(str(t.get("dtype", "")).lower())
|
|
77
|
+
if not dtype:
|
|
78
|
+
return None, f"{case_id}: 未知 dtype {t.get('dtype')!r}"
|
|
79
|
+
attrs = []
|
|
80
|
+
for i in inputs:
|
|
81
|
+
if isinstance(i, dict) and i.get("type") != "tensor" and "name" in i:
|
|
82
|
+
if "value" in i:
|
|
83
|
+
attrs.append(f"{i['name']}={fmt_value(i['value'])}")
|
|
84
|
+
return {
|
|
85
|
+
"case_id": str(case_id),
|
|
86
|
+
"shape": "x".join(str(d) for d in shape),
|
|
87
|
+
"dtype": dtype,
|
|
88
|
+
"attrs": ",".join(attrs),
|
|
89
|
+
}, None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def main() -> int:
|
|
93
|
+
ap = argparse.ArgumentParser(
|
|
94
|
+
description="JSONL 用例 → cases.csv(A1 铁律输入适配)"
|
|
95
|
+
)
|
|
96
|
+
ap.add_argument(
|
|
97
|
+
"--jsonl", required=True, help="JSONL 用例文件(torch-ops-profiler 规范)"
|
|
98
|
+
)
|
|
99
|
+
ap.add_argument("-o", "--output", required=True, help="cases.csv 输出")
|
|
100
|
+
ap.add_argument(
|
|
101
|
+
"--max-cases",
|
|
102
|
+
type=int,
|
|
103
|
+
default=0,
|
|
104
|
+
help="截断上限(0=不截断;>20 取前 20 需告知用户,与 A1 铁律同款)",
|
|
105
|
+
)
|
|
106
|
+
args = ap.parse_args()
|
|
107
|
+
|
|
108
|
+
rows_out, skipped = [], []
|
|
109
|
+
with open(args.jsonl, encoding="utf-8") as f:
|
|
110
|
+
for idx, line in enumerate(f, 1):
|
|
111
|
+
line = line.strip()
|
|
112
|
+
if not line:
|
|
113
|
+
continue
|
|
114
|
+
try:
|
|
115
|
+
row = json.loads(line)
|
|
116
|
+
except json.JSONDecodeError as e:
|
|
117
|
+
skipped.append(f"line {idx}: JSON 解析失败({e})")
|
|
118
|
+
continue
|
|
119
|
+
converted, reason = convert_row(idx, row)
|
|
120
|
+
if converted is None:
|
|
121
|
+
skipped.append(reason)
|
|
122
|
+
else:
|
|
123
|
+
rows_out.append(converted)
|
|
124
|
+
|
|
125
|
+
if not rows_out:
|
|
126
|
+
print("ERROR: 无可转换行(全部 skipped,见 stderr)", file=sys.stderr)
|
|
127
|
+
for s in skipped:
|
|
128
|
+
print(f" ⚠ {s}", file=sys.stderr)
|
|
129
|
+
return 1
|
|
130
|
+
|
|
131
|
+
truncated = False
|
|
132
|
+
if args.max_cases and len(rows_out) > args.max_cases:
|
|
133
|
+
rows_out = rows_out[: args.max_cases]
|
|
134
|
+
truncated = True
|
|
135
|
+
print(
|
|
136
|
+
f"WARN: 用例 {len(rows_out)} > {args.max_cases},取前 {args.max_cases}"
|
|
137
|
+
f"(A1 铁律截断,报告必须告知用户)",
|
|
138
|
+
file=sys.stderr,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
out = Path(args.output)
|
|
142
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
143
|
+
# csv.writer 统一转义(检视 #16 修复):case_id/shape/dtype 来自外部 JSONL
|
|
144
|
+
# 属不可信输入,手写拼接在含逗号/引号时产生错位或非法 CSV
|
|
145
|
+
with open(out, "w", encoding="utf-8", newline="") as f:
|
|
146
|
+
writer = csv.writer(f)
|
|
147
|
+
writer.writerow(["case_id", "shape", "dtype", "attrs"])
|
|
148
|
+
for r in rows_out:
|
|
149
|
+
writer.writerow([r["case_id"], r["shape"], r["dtype"], r["attrs"]])
|
|
150
|
+
|
|
151
|
+
print(
|
|
152
|
+
f"cases.csv → {out}({len(rows_out)} 行"
|
|
153
|
+
f"{'(A1 截断)' if truncated else ''},skipped {len(skipped)})"
|
|
154
|
+
)
|
|
155
|
+
for s in skipped[:10]:
|
|
156
|
+
print(f" ⚠ skipped: {s}", file=sys.stderr)
|
|
157
|
+
if len(skipped) > 10:
|
|
158
|
+
print(f" ⚠ …共 {len(skipped)} 条 skipped", file=sys.stderr)
|
|
159
|
+
return 0
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
if __name__ == "__main__":
|
|
163
|
+
sys.exit(main())
|