已合并
feat: 新增 --append/-a 选项支持追加结果到已有CSV #131
FishPotatoChen创建于 13 天前
feat: 新增 --append/-a 选项支持追加结果到已有CSV #131
已合并
共 6 个文件变更+220-118
| @@ -121,7 +121,8 @@ E2E 默认自动探测 NPU;`--cpu` 强制 CPU 执行。详见 `references/e2e- | |||
| 121 | 121 | ||
| 122 | | 参数 | 缩写 | 说明 | | 122 | | 参数 | 缩写 | 说明 | |
| 123 | |------|------|------| | 123 | |------|------|------| |
| 124 | -| `--output` | `-o` | 输出结果 CSV | | 124 | +| `--output` | `-o` | 输出结果 CSV(覆盖已有文件) | |
| 125 | +| `--append` | `-a` | 追加结果到已有 CSV;表头不匹配时覆盖;与 `-o` 互斥 | | ||
| 125 | 126 | ||
| 126 | ## 快速示例 | 127 | ## 快速示例 |
| 127 | 128 | ||
| @@ -116,7 +116,8 @@ python3 -m ttk list -i cases.csv --op add | |||
| 116 | 116 | ||
| 117 | | 参数 | 缩写 | 默认值 | 说明 | 示例 | | 117 | | 参数 | 缩写 | 默认值 | 说明 | 示例 | |
| 118 | |------|------|--------|------|------| | 118 | |------|------|--------|------|------| |
| 119 | -| `--output` | `-o` | 无 | 输出结果CSV路径 | `-o results.csv` | | 119 | +| `--output` | `-o` | 无 | 输出结果CSV路径(覆盖已有文件),与 `-a` 互斥 | `-o results.csv` | |
| 120 | +| `--append` | `-a` | 无 | 追加结果到已有CSV(表头不匹配则覆盖),与 `-o` 互斥 | `-a results.csv` | | ||
| 120 | | `--title` | | 无 | 自定义输出列 | `--title testcase_name,precision_status` | | 121 | | `--title` | | 无 | 自定义输出列 | `--title testcase_name,precision_status` | |
| 121 | | `--csv-preserve` | | 关闭 | 保留原始CSV表头 | `--csv-preserve` | | 122 | | `--csv-preserve` | | 关闭 | 保留原始CSV表头 | `--csv-preserve` | |
| 122 | | `--print` | | 开启 | 打印摘要信息 | `--print=false` | | 123 | | `--print` | | 开启 | 打印摘要信息 | `--print=false` | |
| @@ -7,23 +7,17 @@ _INTEGER_CLEAN_VALUE = re.compile(r"[+-]?(?:0[xX][0-9a-fA-F]+|0[oO][0-7]+|0[bB][ | |||
| 7 | _FLOAT_CLEAN_VALUE = re.compile(r"[+-]?(?:(?:[0-9]+\.[0-9]*|\.[0-9]+|[0-9]+)(?:[eE][+-]?[0-9]+)?)$") | 7 | _FLOAT_CLEAN_VALUE = re.compile(r"[+-]?(?:(?:[0-9]+\.[0-9]*|\.[0-9]+|[0-9]+)(?:[eE][+-]?[0-9]+)?)$") |
| 8 | 8 | ||
| 9 | 9 | ||
| 10 | -def args_to_switches(args): | 10 | +def _apply_io_args(sw, args): |
| 11 | - from ttk.utilities.classes import SWITCHES | ||
| 12 | - | ||
| 13 | - sw = SWITCHES() | ||
| 14 | - sw.logging_to_file = True | ||
| 15 | - sw.config_path = getattr(args, "config", None) # NEW: 经 SWITCHES pickle 传 worker | ||
| 16 | - sw.provider_filter = getattr(args, "provider", None) # NEW: --provider CLI 过滤器 | ||
| 17 | - | ||
| 18 | - # 无条件加载配置(config_path=None 时走标准路径:default.yaml + ~/.config/ttk + ./ttk.conf.yaml)。 | ||
| 19 | - # 删 get_config lazy fallback 后,这是 parent 侧唯一的 load 入口。 | ||
| 20 | - from ttk.config.loader import load_config | ||
| 21 | - | ||
| 22 | - load_config(sw.config_path) | ||
| 23 | - | ||
| 24 | sw.input_files = [args.input] | 11 | sw.input_files = [args.input] |
| 25 | - sw.output_file_name = args.output | 12 | + if hasattr(args, "append_file") and args.append_file: |
| 13 | + sw.output_file_name = args.append_file | ||
| 14 | + sw.append_mode = True | ||
| 15 | + else: | ||
| 16 | + sw.output_file_name = args.output | ||
| 17 | + sw.append_mode = False | ||
| 26 | 18 | ||
| 19 | + | ||
| 20 | +def _apply_case_selection_args(sw, args): | ||
| 27 | if hasattr(args, "testcase") and args.testcase: | 21 | if hasattr(args, "testcase") and args.testcase: |
| 28 | sw.selected_testcases = args.testcase.split(",") | 22 | sw.selected_testcases = args.testcase.split(",") |
| 29 | if hasattr(args, "testcase_index") and args.testcase_index: | 23 | if hasattr(args, "testcase_index") and args.testcase_index: |
| @@ -31,6 +25,8 @@ def args_to_switches(args): | |||
| 31 | if hasattr(args, "testcase_count") and args.testcase_count is not None: | 25 | if hasattr(args, "testcase_count") and args.testcase_count is not None: |
| 32 | sw.selected_testcase_count = args.testcase_count | 26 | sw.selected_testcase_count = args.testcase_count |
| 33 | 27 | ||
| 28 | + | ||
| 29 | +def _apply_filter_args(sw, args): | ||
| 34 | if hasattr(args, "priority") and args.priority: | 30 | if hasattr(args, "priority") and args.priority: |
| 35 | sw.priorities = _parse_priorities(args.priority) | 31 | sw.priorities = _parse_priorities(args.priority) |
| 36 | if hasattr(args, "operator") and args.operator: | 32 | if hasattr(args, "operator") and args.operator: |
| @@ -38,6 +34,8 @@ def args_to_switches(args): | |||
| 38 | if hasattr(args, "exclude_operator") and args.exclude_operator: | 34 | if hasattr(args, "exclude_operator") and args.exclude_operator: |
| 39 | sw.excluded_operators = tuple(args.exclude_operator.split(",")) | 35 | sw.excluded_operators = tuple(args.exclude_operator.split(",")) |
| 40 | 36 | ||
| 37 | + | ||
| 38 | +def _apply_compare_dump_args(sw, args): | ||
| 41 | if hasattr(args, "random_seed") and args.random_seed is not None: | 39 | if hasattr(args, "random_seed") and args.random_seed is not None: |
| 42 | sw.random_seed = args.random_seed | 40 | sw.random_seed = args.random_seed |
| 43 | if hasattr(args, "input_dist"): | 41 | if hasattr(args, "input_dist"): |
| @@ -46,7 +44,6 @@ def args_to_switches(args): | |||
| 46 | sw.compare_method = args.compare | 44 | sw.compare_method = args.compare |
| 47 | if hasattr(args, "golden_mode"): | 45 | if hasattr(args, "golden_mode"): |
| 48 | sw.golden_mode = args.golden_mode | 46 | sw.golden_mode = args.golden_mode |
| 49 | - | ||
| 50 | if hasattr(args, "dump") and args.dump is not None: | 47 | if hasattr(args, "dump") and args.dump is not None: |
| 51 | _apply_dump_config(sw.dump_config, args.dump) | 48 | _apply_dump_config(sw.dump_config, args.dump) |
| 52 | if hasattr(args, "dump_format"): | 49 | if hasattr(args, "dump_format"): |
| @@ -56,12 +53,15 @@ def args_to_switches(args): | |||
| 56 | if hasattr(args, "xpu_perf") and args.xpu_perf: | 53 | if hasattr(args, "xpu_perf") and args.xpu_perf: |
| 57 | sw.xpu_perf = True | 54 | sw.xpu_perf = True |
| 58 | 55 | ||
| 56 | + | ||
| 57 | +def _apply_plugin_rerun_args(sw, args): | ||
| 59 | if hasattr(args, "plugin") and args.plugin: | 58 | if hasattr(args, "plugin") and args.plugin: |
| 60 | sw.plugin_path = tuple(pathlib.Path(p.strip()).resolve() for p in args.plugin.split(",") if p.strip()) | 59 | sw.plugin_path = tuple(pathlib.Path(p.strip()).resolve() for p in args.plugin.split(",") if p.strip()) |
| 61 | - | ||
| 62 | if hasattr(args, "rerun") and args.rerun: | 60 | if hasattr(args, "rerun") and args.rerun: |
| 63 | sw.rerun_targets = args.rerun.lower().split(",") | 61 | sw.rerun_targets = args.rerun.lower().split(",") |
| 64 | 62 | ||
| 63 | + | ||
| 64 | +def _apply_output_log_args(sw, args): | ||
| 65 | if hasattr(args, "title") and args.title: | 65 | if hasattr(args, "title") and args.title: |
| 66 | sw.custom_columns = args.title.split(",") | 66 | sw.custom_columns = args.title.split(",") |
| 67 | if hasattr(args, "csv_preserve") and args.csv_preserve: | 67 | if hasattr(args, "csv_preserve") and args.csv_preserve: |
| @@ -78,6 +78,9 @@ def args_to_switches(args): | |||
| 78 | sw.TASK_PROFILING = False | 78 | sw.TASK_PROFILING = False |
| 79 | if hasattr(args, "progress_output") and args.progress_output: | 79 | if hasattr(args, "progress_output") and args.progress_output: |
| 80 | sw.progress_output = args.progress_output | 80 | sw.progress_output = args.progress_output |
| 81 | + | ||
| 82 | + | ||
| 83 | +def _apply_device_args(sw, args): | ||
| 81 | if hasattr(args, "device") and args.device is not None: | 84 | if hasattr(args, "device") and args.device is not None: |
| 82 | sw.device_count = args.device | 85 | sw.device_count = args.device |
| 83 | if hasattr(args, "device_blacklist") and args.device_blacklist: | 86 | if hasattr(args, "device_blacklist") and args.device_blacklist: |
| @@ -90,6 +93,9 @@ def args_to_switches(args): | |||
| 90 | sw.dev_plat = args.platform | 93 | sw.dev_plat = args.platform |
| 91 | if hasattr(args, "proc_timeout") and args.proc_timeout: | 94 | if hasattr(args, "proc_timeout") and args.proc_timeout: |
| 92 | sw.proc_timeout = args.proc_timeout | 95 | sw.proc_timeout = args.proc_timeout |
| 96 | + | ||
| 97 | + | ||
| 98 | +def _apply_run_args(sw, args): | ||
| 93 | if hasattr(args, "validate_only") and args.validate_only: | 99 | if hasattr(args, "validate_only") and args.validate_only: |
| 94 | sw.validate_only = True | 100 | sw.validate_only = True |
| 95 | if hasattr(args, "warmup") and not args.warmup: | 101 | if hasattr(args, "warmup") and not args.warmup: |
| @@ -101,6 +107,30 @@ def args_to_switches(args): | |||
| 101 | if hasattr(args, "deterministic_level") and args.deterministic_level: | 107 | if hasattr(args, "deterministic_level") and args.deterministic_level: |
| 102 | sw.deterministic_level = args.deterministic_level | 108 | sw.deterministic_level = args.deterministic_level |
| 103 | 109 | ||
| 110 | + | ||
| 111 | +def args_to_switches(args): | ||
| 112 | + from ttk.utilities.classes import SWITCHES | ||
| 113 | + | ||
| 114 | + sw = SWITCHES() | ||
| 115 | + sw.logging_to_file = True | ||
| 116 | + sw.config_path = getattr(args, "config", None) # NEW: 经 SWITCHES pickle 传 worker | ||
| 117 | + sw.provider_filter = getattr(args, "provider", None) # NEW: --provider CLI 过滤器 | ||
| 118 | + | ||
| 119 | + # 无条件加载配置(config_path=None 时走标准路径:default.yaml + ~/.config/ttk + ./ttk.conf.yaml)。 | ||
| 120 | + # 删 get_config lazy fallback 后,这是 parent 侧唯一的 load 入口。 | ||
| 121 | + from ttk.config.loader import load_config | ||
| 122 | + | ||
| 123 | + load_config(sw.config_path) | ||
| 124 | + | ||
| 125 | + _apply_io_args(sw, args) | ||
| 126 | + _apply_case_selection_args(sw, args) | ||
| 127 | + _apply_filter_args(sw, args) | ||
| 128 | + _apply_compare_dump_args(sw, args) | ||
| 129 | + _apply_plugin_rerun_args(sw, args) | ||
| 130 | + _apply_output_log_args(sw, args) | ||
| 131 | + _apply_device_args(sw, args) | ||
| 132 | + _apply_run_args(sw, args) | ||
| 133 | + | ||
| 104 | return sw | 134 | return sw |
| 105 | 135 | ||
| 106 | 136 | ||
| @@ -3,12 +3,18 @@ import argparse | |||
| 3 | from ttk.remote import is_remote_configured | 3 | from ttk.remote import is_remote_configured |
| 4 | 4 | ||
| 5 | 5 | ||
| 6 | -def add_common_args(parser): | 6 | +def _add_io_args(parser): |
| 7 | parser.add_argument("-i", "--input", required=True, help="CSV test case file") | 7 | parser.add_argument("-i", "--input", required=True, help="CSV test case file") |
| 8 | parser.add_argument( | 8 | parser.add_argument( |
| 9 | "--config", default=None, help="Path to ttk config YAML (overrides ~/.config/ttk/ and ./ttk.conf.yaml)" | 9 | "--config", default=None, help="Path to ttk config YAML (overrides ~/.config/ttk/ and ./ttk.conf.yaml)" |
| 10 | ) | 10 | ) |
| 11 | - parser.add_argument("-o", "--output", help="Output CSV file") | 11 | + output_group = parser.add_mutually_exclusive_group() |
| 12 | + output_group.add_argument("-o", "--output", help="Output CSV file (overwrite existing)") | ||
| 13 | + output_group.add_argument("-a", "--append", dest="append_file", help="Append results to existing CSV file; " | ||
| 14 | + "overwrites if file header does not match") | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +def _add_case_filter_args(parser): | ||
| 12 | parser.add_argument("-t", "--testcase", help="Specify testcase name(s), comma-separated") | 18 | parser.add_argument("-t", "--testcase", help="Specify testcase name(s), comma-separated") |
| 13 | parser.add_argument( | 19 | parser.add_argument( |
| 14 | "--ti", "--testcase-index", dest="testcase_index", help="Specify testcase indexes, e.g. --ti=1,3,6 or --ti=1-5" | 20 | "--ti", "--testcase-index", dest="testcase_index", help="Specify testcase indexes, e.g. --ti=1,3,6 or --ti=1-5" |
| @@ -25,6 +31,9 @@ def add_common_args(parser): | |||
| 25 | default="uniform", | 31 | default="uniform", |
| 26 | help="Input data distribution (default: uniform)", | 32 | help="Input data distribution (default: uniform)", |
| 27 | ) | 33 | ) |
| 34 | + | ||
| 35 | + | ||
| 36 | +def _add_precision_args(parser): | ||
| 28 | parser.add_argument( | 37 | parser.add_argument( |
| 29 | "--compare", | 38 | "--compare", |
| 30 | default=None, | 39 | default=None, |
| @@ -57,8 +66,27 @@ def add_common_args(parser): | |||
| 57 | help="Prepared input/golden data directories. Without --no-prof, " | 66 | help="Prepared input/golden data directories. Without --no-prof, " |
| 58 | "load matching testcase data and run device comparison", | 67 | "load matching testcase data and run device comparison", |
| 59 | ) | 68 | ) |
| 69 | + | ||
| 70 | + | ||
| 71 | +def _add_run_args(parser): | ||
| 60 | parser.add_argument("--plugin", help="External plugin path for customized golden/inputs") | 72 | parser.add_argument("--plugin", help="External plugin path for customized golden/inputs") |
| 61 | parser.add_argument("--rerun", help="Rerun failed cases, e.g. --rerun=precision_status") | 73 | parser.add_argument("--rerun", help="Rerun failed cases, e.g. --rerun=precision_status") |
| 74 | + parser.add_argument( | ||
| 75 | + "--validate", dest="validate_only", action="store_true", help="Validate CSV cases only, skip device execution" | ||
| 76 | + ) | ||
| 77 | + parser.add_argument( | ||
| 78 | + "--proc-no-reuse", dest="proc_no_reuse", action="store_true", help="Create new process for each case" | ||
| 79 | + ) | ||
| 80 | + parser.add_argument( | ||
| 81 | + "--provider", | ||
| 82 | + default=None, | ||
| 83 | + help="Which providers to TEST (e.g. torch,tf). " | ||
| 84 | + "Test filter — narrows dispatch, does NOT override remote config. " | ||
| 85 | + "If not set, uses the first available key from spec's third_party.", | ||
| 86 | + ) | ||
| 87 | + | ||
| 88 | + | ||
| 89 | +def _add_output_args(parser): | ||
| 62 | parser.add_argument( | 90 | parser.add_argument( |
| 63 | "--title", "--titles", dest="title", help="Custom output columns, e.g. --title=testcase_name,dyn_perf_us" | 91 | "--title", "--titles", dest="title", help="Custom output columns, e.g. --title=testcase_name,dyn_perf_us" |
| 64 | ) | 92 | ) |
| @@ -75,12 +103,6 @@ def add_common_args(parser): | |||
| 75 | type=lambda x: x.lower() != "false", | 103 | type=lambda x: x.lower() != "false", |
| 76 | help="Print summary info periodically (default: true)", | 104 | help="Print summary info periodically (default: true)", |
| 77 | ) | 105 | ) |
| 78 | - parser.add_argument( | ||
| 79 | - "--validate", dest="validate_only", action="store_true", help="Validate CSV cases only, skip device execution" | ||
| 80 | - ) | ||
| 81 | - parser.add_argument( | ||
| 82 | - "--proc-no-reuse", dest="proc_no_reuse", action="store_true", help="Create new process for each case" | ||
| 83 | - ) | ||
| 84 | parser.add_argument("--no-memory-check", dest="no_memory_check", action="store_true", help="Skip host memory check") | 106 | parser.add_argument("--no-memory-check", dest="no_memory_check", action="store_true", help="Skip host memory check") |
| 85 | parser.add_argument( | 107 | parser.add_argument( |
| 86 | "--task-prof", | 108 | "--task-prof", |
| @@ -90,13 +112,14 @@ def add_common_args(parser): | |||
| 90 | help="Task-level profiling (msprof) switch (default: true)", | 112 | help="Task-level profiling (msprof) switch (default: true)", |
| 91 | ) | 113 | ) |
| 92 | parser.add_argument("--po", "--progress-output", dest="progress_output", help="Progress output file") | 114 | parser.add_argument("--po", "--progress-output", dest="progress_output", help="Progress output file") |
| 93 | - parser.add_argument( | 115 | + |
| 94 | - "--provider", | 116 | + |
| 95 | - default=None, | 117 | +def add_common_args(parser): |
| 96 | - help="Which providers to TEST (e.g. torch,tf). " | 118 | + _add_io_args(parser) |
| 97 | - "Test filter — narrows dispatch, does NOT override remote config. " | 119 | + _add_case_filter_args(parser) |
| 98 | - "If not set, uses the first available key from spec's third_party.", | 120 | + _add_precision_args(parser) |
| 99 | - ) | 121 | + _add_run_args(parser) |
| 122 | + _add_output_args(parser) | ||
| 100 | 123 | ||
| 101 | 124 | ||
| 102 | def validate_xpu_perf_precondition(sw): | 125 | def validate_xpu_perf_precondition(sw): |
| @@ -84,6 +84,15 @@ class InstanceBase(metaclass=ABCMeta): | |||
| 84 | self.heartbeat_manager = None # HeartbeatManager or None | 84 | self.heartbeat_manager = None # HeartbeatManager or None |
| 85 | self.collected_results: list = [] | 85 | self.collected_results: list = [] |
| 86 | 86 | ||
| 87 | + | ||
| 88 | + def _read_existing_header(path: str): | ||
| 89 | + try: | ||
| 90 | + with open(path, newline='', encoding='utf-8') as f: | ||
| 91 | + reader = csv.reader(f) | ||
| 92 | + return next(reader, None) | ||
| 93 | + except (UnicodeDecodeError, csv.Error, OSError): | ||
| 94 | + return None | ||
| 95 | + | ||
| 87 | 96 | ||
| 88 | def env_prepare(self): | 97 | def env_prepare(self): |
| 89 | pass | 98 | pass |
| @@ -436,10 +445,35 @@ class InstanceBase(metaclass=ABCMeta): | |||
| 436 | f"It will be set as {self.result_path}") | 445 | f"It will be set as {self.result_path}") |
| 437 | if not self.result_path.endswith('.csv'): | 446 | if not self.result_path.endswith('.csv'): |
| 438 | self.result_path += '.csv' | 447 | self.result_path += '.csv' |
| 439 | - self.result_csv_file = open(self.result_path, newline='', mode='w+') | 448 | + parent_dir = os.path.dirname(self.result_path) |
| 440 | - self.result_csv_writer = csv.writer(self.result_csv_file) | 449 | + if parent_dir and not os.path.isdir(parent_dir): |
| 441 | - self._prepare_output_titles() | 450 | + os.makedirs(parent_dir, exist_ok=True) |
| 442 | - self._flush(self.case_result_titles) | 451 | + logging.info(f"Created output directory: {parent_dir}") |
| 452 | + append_mode = self.switches.append_mode | ||
| 453 | + file_exists = os.path.exists(self.result_path) and os.path.getsize(self.result_path) > 0 | ||
| 454 | + if append_mode and file_exists: | ||
| 455 | + self._prepare_output_titles() | ||
| 456 | + existing_header = self._read_existing_header(self.result_path) | ||
| 457 | + header_match = existing_header is not None and tuple(existing_header) == self.case_result_titles | ||
| 458 | + if header_match: | ||
| 459 | + self.result_csv_file = open(self.result_path, newline='', mode='a+') | ||
| 460 | + self.result_csv_writer = csv.writer(self.result_csv_file) | ||
| 461 | + self._header_flushed = True | ||
| 462 | + self._precision_status_idx = self._resolve_precision_status_idx(self.case_result_titles) | ||
| 463 | + logging.info(f"Append mode: appending to existing {self.result_path}") | ||
| 464 | + else: | ||
| 465 | + logging.warning(f"Append mode: existing file header does not match " | ||
| 466 | + f"(file has {len(existing_header) if existing_header else 0} columns, " | ||
| 467 | + f"current expects {len(self.case_result_titles)}). " | ||
| 468 | + f"Overwriting {self.result_path}") | ||
| 469 | + self.result_csv_file = open(self.result_path, newline='', mode='w+') | ||
| 470 | + self.result_csv_writer = csv.writer(self.result_csv_file) | ||
| 471 | + self._flush(self.case_result_titles) | ||
| 472 | + else: | ||
| 473 | + self.result_csv_file = open(self.result_path, newline='', mode='w+') | ||
| 474 | + self.result_csv_writer = csv.writer(self.result_csv_file) | ||
| 475 | + self._prepare_output_titles() | ||
| 476 | + self._flush(self.case_result_titles) | ||
| 443 | 477 | ||
| 444 | def _prepare_output_titles(self): | 478 | def _prepare_output_titles(self): |
| 445 | first_testcase = next(iter(self.flatten_testcases)) | 479 | first_testcase = next(iter(self.flatten_testcases)) |
| @@ -105,6 +105,7 @@ class SWITCHES: | |||
| 105 | "mode", | 105 | "mode", |
| 106 | "input_files", | 106 | "input_files", |
| 107 | "output_file_name", | 107 | "output_file_name", |
| 108 | + "append_mode", | ||
| 108 | "logging_to_file", | 109 | "logging_to_file", |
| 109 | "single_testcase_log_mode", | 110 | "single_testcase_log_mode", |
| 110 | "dev_plat", | 111 | "dev_plat", |
| @@ -181,86 +182,10 @@ class SWITCHES: | |||
| 181 | ] | 182 | ] |
| 182 | 183 | ||
| 183 | def __init__(self): | 184 | def __init__(self): |
| 184 | - self.root_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | 185 | + self._init_paths_and_mode() |
| 185 | - self.mode: MODE = MODE.ASCEND_ONBOARD | 186 | + self._init_device_and_run() |
| 186 | - self.input_files: Optional[List[str]] = None | 187 | + self._init_testcases_and_filters() |
| 187 | - self.output_file_name: Optional[str] = None | 188 | + self._init_modes_and_backend() |
| 188 | - self.logging_to_file: bool = False | ||
| 189 | - self.single_testcase_log_mode = False | ||
| 190 | - self.dev_plat: str = "AUTO" | ||
| 191 | - self.short_soc_version: Optional[str] = None # None in non-NPU mode | ||
| 192 | - self.custom_columns = None | ||
| 193 | - self.print_help: bool = False | ||
| 194 | - self.process_per_device = None | ||
| 195 | - self.dyn_switches: OPTestSwitch = OPTestSwitch("Dynamic Shape", True, True, True) | ||
| 196 | - self.cst_switches: OPTestSwitch = OPTestSwitch("Const Shape", False, True, True) | ||
| 197 | - self.bin_switches: OPTestSwitch = OPTestSwitch("Binary Release", False, True, True) | ||
| 198 | - self.rerun_targets = None | ||
| 199 | - self.TASK_PROFILING = True | ||
| 200 | - self.dump_config = DumpConfig() | ||
| 201 | - self.device_count = -1 | ||
| 202 | - self.device_blacklist = [] | ||
| 203 | - self.device_whitelist = [] | ||
| 204 | - self.run_timeout = 0 | ||
| 205 | - self.proc_timeout = 0 | ||
| 206 | - self.tiling_run_time = 3 | ||
| 207 | - self.no_memory_check = False | ||
| 208 | - self.force_clear_atomic = [None, None, None] | ||
| 209 | - self.force_block_dim = [None, None, None] | ||
| 210 | - self.force_clear_ub = None | ||
| 211 | - self.force_clear_l1 = None | ||
| 212 | - self.force_simt_ub_size = [None, None, None] # dynamic/const/binary | ||
| 213 | - self.proc_no_reuse = False | ||
| 214 | - # Hidden switches | ||
| 215 | - self.kernel_meta = os.path.join(self.root_path, "kernel_meta") | ||
| 216 | - self.warmup = True | ||
| 217 | - self.summary_print = True | ||
| 218 | - # Constants | ||
| 219 | - self.DAVINCI_HBM_SIZE_LIMIT = 30 # GB | ||
| 220 | - # Testcases | ||
| 221 | - self.selected_testcases = [] | ||
| 222 | - self.selected_testcase_indexes = [] | ||
| 223 | - self.selected_testcase_count = -1 | ||
| 224 | - self.selected_operators = None | ||
| 225 | - self.excluded_operators = None | ||
| 226 | - self.preserve_original_csv = False | ||
| 227 | - self.random_seed = None | ||
| 228 | - self.progress_output = None | ||
| 229 | - self.op_impl_mode: Optional[str] = None | ||
| 230 | - self.simt_cfg: SoCSimtCfg = SoCSimtCfg() | ||
| 231 | - self.input_distribution: str = "uniform" | ||
| 232 | - self.golden_mode: str = "Enable" | ||
| 233 | - self.compare_method = None | ||
| 234 | - self.xpu_perf: bool = False | ||
| 235 | - self.precision_report: Optional[str] = None | ||
| 236 | - self.reuse_hbm: bool = False | ||
| 237 | - self.reserve_hbm: int = 0 | ||
| 238 | - self.priorities: Optional[tuple] = None | ||
| 239 | - self.compile_options: dict = {} | ||
| 240 | - self.plugin_path: Optional[Tuple[pathlib.Path]] = None | ||
| 241 | - self.test_mode: str = "op" | ||
| 242 | - self.force_cpu: bool = False | ||
| 243 | - self.fullgraph: int = 0 | ||
| 244 | - self.aclgraph_enabled: bool = False | ||
| 245 | - self.validate_only: bool = False | ||
| 246 | - self.manual_data_mode: Optional[str] = None | ||
| 247 | - self.manual_data_dirs: Tuple[str, ...] = () | ||
| 248 | - # private properties below | ||
| 249 | - self._run_time: Optional[int] = None | ||
| 250 | - self._compile_only: bool = False | ||
| 251 | - self.config_path: Optional[str] = None | ||
| 252 | - self.provider_filter: Optional[str] = None | ||
| 253 | - # GEIR mode | ||
| 254 | - self.geir_binary: bool = False | ||
| 255 | - self.deterministic_level: int = 0 | ||
| 256 | - # NPUSim simulator backend | ||
| 257 | - self.backend: str = "npu" # "npu" | "npusim" | ||
| 258 | - self.sim_soc_version: str = "Ascend950" | ||
| 259 | - self.sim_output_dir: str = "" # 空 -> root_path/sim_output | ||
| 260 | - self.sim_report: bool = False | ||
| 261 | - self.sim_cores: str = "" | ||
| 262 | - self.sim_object_file: str = "" | ||
| 263 | - self.framework: str = "torch" | ||
| 264 | 189 | ||
| 265 | def __getstate__(self): | 190 | def __getstate__(self): |
| 266 | """Pickle 支持:仅导出 __slots__ 中已赋值的属性(跳过 property/私有)。""" | 191 | """Pickle 支持:仅导出 __slots__ 中已赋值的属性(跳过 property/私有)。""" |
| @@ -314,6 +239,94 @@ class SWITCHES: | |||
| 314 | def oom_enabled(self) -> bool: | 239 | def oom_enabled(self) -> bool: |
| 315 | return "oom" in self.compile_options.get("op_debug_config", "") | 240 | return "oom" in self.compile_options.get("op_debug_config", "") |
| 316 | 241 | ||
| 242 | + def _init_paths_and_mode(self): | ||
| 243 | + self.root_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | ||
| 244 | + self.mode: MODE = MODE.ASCEND_ONBOARD | ||
| 245 | + self.input_files: Optional[List[str]] = None | ||
| 246 | + self.output_file_name: Optional[str] = None | ||
| 247 | + self.append_mode: bool = False | ||
| 248 | + self.logging_to_file: bool = False | ||
| 249 | + self.single_testcase_log_mode = False | ||
| 250 | + self.dev_plat: str = "AUTO" | ||
| 251 | + self.short_soc_version: Optional[str] = None # None in non-NPU mode | ||
| 252 | + self.custom_columns = None | ||
| 253 | + self.print_help: bool = False | ||
| 254 | + self.process_per_device = None | ||
| 255 | + self.dyn_switches: OPTestSwitch = OPTestSwitch("Dynamic Shape", True, True, True) | ||
| 256 | + self.cst_switches: OPTestSwitch = OPTestSwitch("Const Shape", False, True, True) | ||
| 257 | + self.bin_switches: OPTestSwitch = OPTestSwitch("Binary Release", False, True, True) | ||
| 258 | + self.rerun_targets = None | ||
| 259 | + self.TASK_PROFILING = True | ||
| 260 | + self.dump_config = DumpConfig() | ||
| 261 | + | ||
| 262 | + def _init_device_and_run(self): | ||
| 263 | + self.device_count = -1 | ||
| 264 | + self.device_blacklist = [] | ||
| 265 | + self.device_whitelist = [] | ||
| 266 | + self.run_timeout = 0 | ||
| 267 | + self.proc_timeout = 0 | ||
| 268 | + self.tiling_run_time = 3 | ||
| 269 | + self.no_memory_check = False | ||
| 270 | + self.force_clear_atomic = [None, None, None] | ||
| 271 | + self.force_block_dim = [None, None, None] | ||
| 272 | + self.force_clear_ub = None | ||
| 273 | + self.force_clear_l1 = None | ||
| 274 | + self.force_simt_ub_size = [None, None, None] # dynamic/const/binary | ||
| 275 | + self.proc_no_reuse = False | ||
| 276 | + # Hidden switches | ||
| 277 | + self.kernel_meta = os.path.join(self.root_path, "kernel_meta") | ||
| 278 | + self.warmup = True | ||
| 279 | + self.summary_print = True | ||
| 280 | + # Constants | ||
| 281 | + self.DAVINCI_HBM_SIZE_LIMIT = 30 # GB | ||
| 282 | + | ||
| 283 | + def _init_testcases_and_filters(self): | ||
| 284 | + self.selected_testcases = [] | ||
| 285 | + self.selected_testcase_indexes = [] | ||
| 286 | + self.selected_testcase_count = -1 | ||
| 287 | + self.selected_operators = None | ||
| 288 | + self.excluded_operators = None | ||
| 289 | + self.preserve_original_csv = False | ||
| 290 | + self.random_seed = None | ||
| 291 | + self.progress_output = None | ||
| 292 | + self.op_impl_mode: Optional[str] = None | ||
| 293 | + self.simt_cfg: SoCSimtCfg = SoCSimtCfg() | ||
| 294 | + self.input_distribution: str = "uniform" | ||
| 295 | + self.golden_mode: str = "Enable" | ||
| 296 | + self.compare_method = None | ||
| 297 | + self.xpu_perf: bool = False | ||
| 298 | + self.precision_report: Optional[str] = None | ||
| 299 | + self.reuse_hbm: bool = False | ||
| 300 | + self.reserve_hbm: int = 0 | ||
| 301 | + self.priorities: Optional[tuple] = None | ||
| 302 | + self.compile_options: dict = {} | ||
| 303 | + self.plugin_path: Optional[Tuple[pathlib.Path]] = None | ||
| 304 | + | ||
| 305 | + def _init_modes_and_backend(self): | ||
| 306 | + self.test_mode: str = "op" | ||
| 307 | + self.force_cpu: bool = False | ||
| 308 | + self.fullgraph: int = 0 | ||
| 309 | + self.aclgraph_enabled: bool = False | ||
| 310 | + self.validate_only: bool = False | ||
| 311 | + self.manual_data_mode: Optional[str] = None | ||
| 312 | + self.manual_data_dirs: Tuple[str, ...] = () | ||
| 313 | + # private properties below | ||
| 314 | + self._run_time: Optional[int] = None | ||
| 315 | + self._compile_only: bool = False | ||
| 316 | + self.config_path: Optional[str] = None | ||
| 317 | + self.provider_filter: Optional[str] = None | ||
| 318 | + # GEIR mode | ||
| 319 | + self.geir_binary: bool = False | ||
| 320 | + self.deterministic_level: int = 0 | ||
| 321 | + # NPUSim simulator backend | ||
| 322 | + self.backend: str = "npu" # "npu" | "npusim" | ||
| 323 | + self.sim_soc_version: str = "Ascend950" | ||
| 324 | + self.sim_output_dir: str = "" # 空 -> root_path/sim_output | ||
| 325 | + self.sim_report: bool = False | ||
| 326 | + self.sim_cores: str = "" | ||
| 327 | + self.sim_object_file: str = "" | ||
| 328 | + self.framework: str = "torch" | ||
| 329 | + | ||
| 317 | 330 | ||
| 318 | class OPTestSwitch: | 331 | class OPTestSwitch: |
| 319 | """ | 332 | """ |