Files
MoviePilot/scripts/perf/README.md
T

194 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MoviePilot Docker A/B Harness
该工具用同一个冻结 Docker substrate 对两个 Git commit 做源码级 A/B。派生镜像会先清空
`/app`,复制目标 commit 的完整 `git archive`,再从 substrate 注入镜像构建阶段生成的插件目录、
`sites.*.so``user.sites.v3.bin`。如果依赖或 Docker substrate 输入发生变化,工具会拒绝继续,
避免把外部构建输入漂移误算成性能收益。
工具不会读取工作区或容器内的 `app.env`,不挂载真实配置、媒体目录或 Docker socket。固定配置只使用
内置实验室占位凭据,样本不发布主机端口,并运行在无外网的 internal network。
## 一次性预热浏览器
默认从固定命名 volume `mp-perf-v3-browser-seed` 复制 CloakBrowser 缓存。该 volume 只需联网预热
一次:
```bash
docker volume create mp-perf-v3-browser-seed
docker run --rm \
--mount source=mp-perf-v3-browser-seed,target=/moviepilot/.cloakbrowser \
--entrypoint python3 \
jxxghp/moviepilot-v3@sha256:925de1fdf1bb0312144bc818bc8ebaa999a9a159c6d14f1b48b0ff05edb7f720 \
-m cloakbrowser install
```
也可以在 `seed``run` 时显式传入 `--allow-browser-download`,但该选项会让 seed 阶段联网;
正式 Before/After 样本仍然只克隆预热结果并使用 internal network。
## 分阶段执行
所有全局参数必须放在子命令前。结果默认写到系统临时目录下的
`moviepilot-perf-results/<campaign>/`
```bash
PYTHON=../.venv/bin/python
CAMPAIGN=v3-perf-001
${PYTHON} scripts/perf/moviepilot_docker_ab.py \
--campaign "${CAMPAIGN}" \
build --before-ref upstream/v3 --after-ref HEAD
${PYTHON} scripts/perf/moviepilot_docker_ab.py \
--campaign "${CAMPAIGN}" \
seed
${PYTHON} scripts/perf/moviepilot_docker_ab.py \
--campaign "${CAMPAIGN}" \
sample --variant before --index 1 --points 1,5,10,30
```
开发 harness 时可以用小数分钟做短冒烟,例如 `--points 0,0.02`。正式数据必须保持
`1,5,10,30`
未指定 `--scenario` 时仍使用 `idle-default`,样本目录和 Docker 资源名称与既有命令保持一致。
## 浏览器激活场景
浏览器场景使用 campaign browser seed 的独立克隆卷,不直接挂载或写入固定来源卷,也不会在样本
阶段下载浏览器。容器保持 internal network;探针只发送信号,`app.sdk.browser` 的导入、浏览器上下文
创建和本地 `data:` 页面校验都发生在主 MoviePilot Python 进程中。
非默认浏览器场景用于候选实现的 After 激活门禁;Before 不具备新 SDK,且旧实现启动时已经常驻
Xvfb,因此不能用同一个 `0 → 0` / `0 → 1` 不变量衡量。三轮 Before/After 空载收益仍由默认
`run``idle-default` 场景完成,浏览器场景用三个隔离的 After sample 记录冷激活成本。
```bash
../.venv/bin/python scripts/perf/moviepilot_docker_ab.py \
--campaign v3-perf-002-headless \
sample --variant after --index 1 --scenario browser-headless --points 1,5,10,30
../.venv/bin/python scripts/perf/moviepilot_docker_ab.py \
--campaign v3-perf-002-headed \
sample --variant after --index 1 --scenario browser-headed --points 1,5,10,30
```
- `browser-headless`:一次真实 headless context 激活,要求 Xvfb `0 → 0`
- `browser-headed`:主进程内两个线程通过屏障并发调用
`launch_browser_context(headless=False)`;要求两个真实 SDK 冷启动调用成功、额外上下文关闭后只保留一个、
Capability observation 只有一个 `headed_browser_launch` generation/start,且 Xvfb `0 → 1`
- 激活完成后再开始 `1/5/10/30m` 计时,JSON 保留激活前后 Engine 网络、working set、进程
PSS/USS/RSS/线程、Xvfb 数量/PSS、`sys.modules` 和进程内 marker
- 非默认场景结果保存在 `samples/<scenario>/<variant>-<index>/`,可与同 campaign 的 idle 样本并存,
Markdown 中位数会按场景分组,不会混算。
## Agent 惰性物化场景
PERF-003 在既有 `AI_AGENT_ENABLE=false` 固定配置下增加两个 After-only 场景。探针只向主 MoviePilot
Python 进程发送信号;OpenAPI 生成和工具目录构造均发生在该解释器内,不通过 `docker exec` 启动
第二个 Python,也不调用真实 Agent、LLM provider 或外部 MCP。
先以 `f2e548e1` 冻结 Before,候选提交完成后把 `AFTER_COMMIT` 替换为其精确 commit
```bash
../.venv/bin/python scripts/perf/moviepilot_docker_ab.py \
--campaign v3-perf-003 \
build --before-ref f2e548e1 --after-ref AFTER_COMMIT
../.venv/bin/python scripts/perf/moviepilot_docker_ab.py \
--campaign v3-perf-003 \
seed --browser-source-volume mp-perf-v3-browser-seed --replace
```
正式 idle-default 三组 A/B 仍使用原 `run` 合同;下面两个动作场景在同一 build/seed 后单独采 After
不会覆盖 idle 结果:
```bash
../.venv/bin/python scripts/perf/moviepilot_docker_ab.py \
--campaign v3-perf-003 \
run --before-ref f2e548e1 --after-ref AFTER_COMMIT \
--browser-source-volume mp-perf-v3-browser-seed \
--points 1,5,10,30 --replace --keep-resources
../.venv/bin/python scripts/perf/moviepilot_docker_ab.py \
--campaign v3-perf-003 \
sample --variant after --index 1 --scenario agent-disabled-router --points 1,5,10,30
../.venv/bin/python scripts/perf/moviepilot_docker_ab.py \
--campaign v3-perf-003 \
sample --variant after --index 2 --scenario agent-tool-catalog --points 1,5,10,30
```
- `agent-disabled-router`:直接从主进程 FastAPI app 生成完整 OpenAPI,确认 Agent、LLM、MCP、OpenAI、
Anthropic 路由在禁用态仍存在,同时 callback、LLM helper、工具域、orchestrator、LangGraph 和 provider SDK
前后保持 0,工具工厂不物化;
- `agent-tool-catalog`:通过主进程已有的 `moviepilot_tool_manager.list_tools()` 首次构建现有工具目录和 JSON Schema
要求动作前工具域未物化,动作后仅工具 base/catalog/factory/impl 物化;目录还必须无身份碰撞、Schema
digest 完整,重复读取复用同一 snapshot/revision。结果记录工具数、Schema 摘要、plugin revision 与
factory revision
- 固定哨兵覆盖 `app.agent.orchestrator``app.agent.callback``app.agent.llm.helper`、工具
`base/catalog/factory/impl``langgraph``langchain``langchain_core``openai``anthropic`
`google.genai``boto3``botocore`。其中 `langchain/langchain_core` 可能由完整 Schema 聚合形成既有
基线,只记录数量与变化,不作为禁用态归零门禁;
- JSON 保留动作前后 Engine、PSS/USS、线程、完整 `sys.modules`、materialization observation、revision、
网络累计值和浏览器卷指纹;动作前后容器网络收发必须为 0,Markdown 另汇总 Agent 场景与各定时点的
模块哨兵峰值;
- 启用态 Agent 生命周期不会在该无凭据场景中伪造。现有 `get_running_agent_manager()` 是严格只读、
non-materializing 的运行态 getter`begin_agent_shutdown()` 也只是关闭轴;二者都不是安全启用入口。
启用态必须由正式 startup/service lifecycle 驱动,只有宿主形成明确不创建 provider/client、不会外联的
公共初始化合同后,才适合加入同一测量门禁。
## 完整三组 A/B
```bash
../.venv/bin/python scripts/perf/moviepilot_docker_ab.py \
--campaign v3-perf-001 \
run \
--before-ref upstream/v3 \
--after-ref HEAD \
--points 1,5,10,30
```
执行顺序固定为:
```text
Before-1 → After-1 → After-2 → Before-2 → Before-3 → After-3
```
每个样本都从 SQLite 和浏览器 seed 克隆新的命名 volume。样本结束后立即移除容器和样本卷;
完整 `run` 结束后还会移除 campaign seed 与 internal network。派生镜像和本地结果保留,便于复核。
## 输出
```text
<output>/<campaign>/
├── build.json
├── seed.json
├── results.json
├── report.md
└── samples/
├── before-1/
│ ├── result.json
│ ├── container.log
│ └── modules/
└── ...
```
- `results.json`Engine stats、进程 PSS/USS/RSS/线程、网络累计值和模块前缀计数;
- `report.md`:三次原值与中位数 Before/After 汇总;
- `modules/`:由目标 MoviePilot Python 进程自身写出的完整 `sys.modules` 名称清单;
- `container.log`:已移除实验室凭据值和本地实例 UUID。
容器 working set 统一按 Docker Engine API 的
`memory_stats.usage - memory_stats.stats.inactive_file` 计算。进程 PSS 仅用于归因,不能替代容器指标。
## 清理
清理严格限定到 campaign 标签;不会执行 Docker prune
```bash
../.venv/bin/python scripts/perf/moviepilot_docker_ab.py \
--campaign v3-perf-001 cleanup --images
```
本地 JSON、Markdown 和日志不会被 `cleanup` 删除。