Closed
[Bug-Report|缺陷反馈] 失败或中断的benchmark会话仍覆写性能基线 #8
tianjunAdd on 29 days agoClosed at 28 days ago
tianjun
29 days ago Comments:
29 days ago Comments:
/assign @Ethan_Zou


29 days ago Assign Ethan_Zou as responsible person
28 days ago Added label: resolved
场景与影响
pytest_sessionfinish在pytest会话失败/fixture错误/中断后仍无条件调用_update_baselines,--refresh-baseline会把已收集记录覆写原有JSONL基线,失败运行也污染性能参考值。
版本与位置
master
af459ebfa36cf7f15532cc63385e8d4289704adb;生产位置tests/pytest_benchmark_plugin.py。复现与验证
在精确基线
af459ebfa36cf7f15532cc63385e8d4289704adb与同一最终回归文件上,真实 pytest 原版 3 failed、1 passed;最终 4 passed,0 skip。完整原始插件 sessionfinish/file write 路径和实际嵌套pytest会话,sessionfinish收到测试失败/fixture错误/KeyboardInterrupt/零四种状态;JSONL按字节核对。守卫只判断该hook收到的exitstatus,不承诺覆盖后续终端报告或其他插件的错误。公开 CPU 复现(基线或修复仓库根目录):
python3 -m pip install pytest "torch==2.7.1" PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 TORCH_DEVICE_BACKEND_AUTOLOAD=0 python3 - <<'PY' import ast import importlib.util from pathlib import Path import sys import types import pytest root = Path.cwd() import json import tempfile directory_owner = tempfile.TemporaryDirectory(prefix="tilelang-benchmark-host-") directory = Path(directory_owner.name) (directory / "pytest.ini").write_text("[pytest]\n") record = dict(kernel="host_fixture", operation="record", params={}, param_id=None, cluster="a5", time_us=1.0, bandwidth_gbs=1.0) baseline = directory / "benchmark/test_cpu_refresh.jsonl" baseline.parent.mkdir() original = json.dumps(record) + "\n" baseline.write_text(original) (directory / "test_cpu_refresh.py").write_text( 'import pytest\n' 'def test_record(benchmark_record):\n' ' benchmark_record(kernel="host_fixture", operation="record", params={}, time_us=2.0, bandwidth_gbs=1.0)\n' ' pytest.fail("controlled failed session")\n') sys.path[:0] = [str(root), str(root / "src")] # Isolate unrelated package kernel imports; every exercised plugin method remains real. package = types.ModuleType("cann_ops_tilelang") package.__path__ = [str(root / "src/cann_ops_tilelang")] package.__spec__ = importlib.util.spec_from_loader(package.__name__, loader=None, is_package=True) sys.modules[package.__name__] = package path = root / "tests/pytest_benchmark_plugin.py" tree = ast.parse(path.read_text()) excluded = [node for node in tree.body if isinstance(node, ast.ImportFrom) and node.module == "tilelang.profiler.bench"] assert len(excluded) == 1 # The timer backend is unavailable without TileLang/NPU and is never called here. tree.body = [node for node in tree.body if node not in excluded] module = types.ModuleType("host_benchmark_plugin") module.__file__ = str(path) sys.modules[module.__name__] = module exec(compile(tree, str(path), "exec"), vars(module)) setattr(module, "_TESTS_DIR", str(directory)) setattr(module, "_BASELINES_LAYOUT", str(directory / "benchmark")) args = [str(directory / "test_cpu_refresh.py"), "-q", "-c", str(directory / "pytest.ini"), "--confcutdir=" + str(directory), "--run-benchmark", "--refresh-baseline", "--benchmark-output=" + str(directory / "results.jsonl")] try: status = pytest.main(args, plugins=[module]) except KeyError as exc: print("KeyError:", str(exc)) status = 1 print("session_exit=", int(status)) print("baseline_time_us=", json.loads(baseline.read_text())["time_us"]) directory_owner.cleanup() PY公开复现原版 session_exit=1 却 baseline_time_us=2.0;修复后同为失败退出,但基线保持1.0。
关联 PR 拟新增
tests/host/test_benchmark_refresh_failed_session.py;将同一文件复制到精确基线或检出修复分支,执行PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 TORCH_DEVICE_BACKEND_AUTOLOAD=0 python3 -m pytest -q tests/host/test_benchmark_refresh_failed_session.py --confcutdir=tests/host。只排除一个未调用的 tilelang.profiler.bench 后端导入,并隔离无关 kernel 包初始化;执行的插件源码/pytest hooks/fixture/JSONL文件操作均为真实实现。受控时间字段用于Host文件/报告行为验证,不是实测NPU性能;没有模拟NPU计时,没有调用benchmark_timer。最终实际仓库 Ruff 检查/格式化、diff --check通过。
预期与修复建议
仅在pytest_sessionfinish收到exitstatus=0时更新基线;该hook收到测试失败、fixture错误或中断状态时保留基线和结果输出。新增真实pytest状态/文件事务回归与精确边界文档。
本地无 NPU;仅核查实际源码及 Host 行为,未运行 CANN/NPU 编译、设备精度或性能测试。