pytest-xdist并行加速需要避免资源竞争,-n auto不等于最优,核数越多越快;应该从-n 2起步测试,I//O密集型应设置物理核数×1.5,配合--dist=loadscope等策略及fixture隔离设计。
因为 pytest-xdist 将原本串行执行的测试用例分发到多个独立过程中并行运行——前提是测试本身不抢资源,不共享状态;否则会增加 -n 反而比较慢,比较不稳定。
为什么 -n auto 不代表“开得越多越快”它调用 os.cpu_count(),但物理核数 ≠ 实际并发收入点。笔记本上开满了。 16 个 worker,可能因磁盘 I/O 或者内存带宽争夺卡 Waiting for workers;CI 环境(如 GitHub Actions)默认只暴露 2 个逻辑核,-n auto 却可能起 4 一、直接超配。
- 推荐从
-n 2开始,逐步尝试-n 4或-n 6,观察 CPU 利用率和总耗时拐点 - I/O 密集型测试(如 HTTP 请求、DB 查询)通常
-n值设置为物理核数 × 1.5 更稳定,而不是盲目追求逻辑核数 - Docker 容器需要显式传输
--cpus=4,否则os.cpu_count()返回 1,-n auto就只起 1 个 worker
不是 xdist 不,测试代码不适合多过程环境。例如 @pytest.fixture(scope="session") 在每个 worker 中都将单独执行一次,如果它最初是一个整体 SQLite 连接或写下路径的临时文件会触发 sqlite3.OperationalError: database is locked 或 PermissionError: [WinError 32]。
- 必须使用所有临时路径
tempfile.mkdtemp()或tmp_path(不是tmpdir),并拼入request.config.workerinput["workerid"]确保隔离 - 避免在
conftest.py的session或modulescope 做非幂等操作,比如修改os.environ、编写配置文件,启动单项服务 - 共享只读数据(例如 JSON Schema)可用
scope="session",但返回值必须是dict/str等可序列化类型
默认 --dist=load 按 test function 均匀分发,但如果是一个 test_heavy.py 文件里塞了 200 一个慢用例,它将被整个分配给一个 worker,其他 worker 空转-负载严重不均匀。
Python数据分析助手
为业务和科研数据的快速处理提供Python数据清理、统计分析和可视化建议。
下载立即学习“Python免费学习笔记(深入);
-
--dist=loadfile:整份test_xxx.py分给一个 worker,适用于文件内的共享 setup/teardown 的场景 -
--dist=loadscope:按 class 或 module 分组,确保相同的模块 fixture 不跨过程冲突,对含 DB 初始逻辑的测试更稳定 - 别用
--dist=load跑含 sleep 或外部 API 调用的测试很容易导致 worker 长时间空闲
Windows 默认用 spawn 方式启动 worker 过程,会重新开始 import 如果主模块直接在主模块中。 import torch 或 import cv2,就可能报 AttributeError: module '__main__' has no attribute '__file__'。
- 把 heavy import 移到具体 test function 内部,或用
if __name__ == "__main__":包裹 - 或者改用
fork(仅限 Linux/macOS),但 CI 大多数环境不支持 - 更安全的做法:在 conftest.py 里用
pytest_configure做条件加载,避免主模块顶层 import
加速的边界非常清晰:CPU 密集型测试收入最大,I/O 其次,密集型,真正卡住的往往不是如何打开过程,而是每个测试是否真的“干净”——是否有秘密共享文件句柄、数据库连接、全球计数器或系统时间。