github-actions[bot]
Deploy 7688ef1
13d4e44
|
Raw
History Blame Contribute Delete
7.01 kB
# Co-Study4Grid — Performance benchmarks
Consolidated micro-benchmarks that exercise the critical path of a
Load Study on the reference **PyPSA-EUR France 400 kV** grid
(~25 MB SVG, 6 835 VLs, 85 304 switches, 14 880 loads+generators,
55 104 operational-limit entries).
These scripts drive the same code paths as the web UI but without the
HTTP stack, so they can be re-run after a patch to catch regressions
before pushing.
## Prerequisites
```bash
# Same venv as the backend (`expert_backend`) — needs:
# pypowsybl, expert_op4grid_recommender, pandas, numpy
export PATH="$HOME/.asdf/shims:$PATH"
python -c "import pypowsybl, expert_op4grid_recommender"
```
Override the reference grid / action file via env vars:
```bash
export BENCH_NETWORK_PATH=/path/to/grid_dir # contains grid.xiidm + grid_layout.json
export BENCH_ACTION_FILE=/path/to/reduced_actions.json
export BENCH_CONTINGENCY=DISCO_NAME # only for bench_n1_diagram.py
```
## Scripts
| Script | What it measures | Where the patch history lives |
|---|---|---|
| `bench_load_study.py` | Full `/api/config` + 4 parallel XHRs round-trip (reset + load_network + update_config + 4 response helpers). Cumulative target of every patch on the branch. | `docs/performance/history/loading-parallel.md` |
| `bench_topology_cache.py` | Per-helper + full `NetworkTopologyCache(net)` init. Validates upstream vectorisation series (0.2.0.post3 → post8). | `docs/performance/history/vectorize-topology-cache.md`, `docs/performance/history/topology-cache-iter2.md` |
| `bench_voltage_level_queries.py` | `/api/voltage-levels`, `/api/nominal-voltages`, `get_monitored_elements`, `_get_switches_with_topology` narrow-attr wins. | `docs/performance/history/narrow-voltage-level-queries.md` |
| `bench_n1_diagram.py` | Full `get_n1_diagram(contingency)` cold + warm, per-sub-step breakdown. Validates the 3 N-1 fast-path patches. | `docs/performance/history/n1-diagram-fast-path.md` |
| `bench_nad_n_state.py` | `get_network_diagram()` cold + warm on the N-state. Captures NAD / SVG / Meta sub-timings from the `[RECO]` log lines. | `docs/performance/nad-profile-bare-env.md` |
| `bench_nad_toggles.py` | Matrix of `NadParameters` toggle combinations — quantifies per-flag impact on NAD gen + SVG size, surfaces the cost of `injections_added=True`. | `docs/performance/nad-profile-bare-env.md` |
| `bench_analyze_suggest.py` | **Full "Analyze & Suggest" for a Game Mode study** — drives `/api/config``step1``step2` (streaming NDJSON) through the FastAPI `TestClient` and prints the UI's execution-time breakdown (step1 / overflow / prediction / **assessment** / enrichment / **Other**), with "Other" decomposed into discovery-overhead / result `sanitize_for_json` / transport. `--serial` forces serial reassessment; `--compare` runs parallel-vs-serial. This is the case the 30 s → 75 s regression was reported on. | `docs/performance/history/analyze-suggest-2vcpu.md` |
| `bench_load_flow_modes.py` | **Per-LF knob sweep** — times a single `run_ac` on the N-1 state under each load-flow parameter variant (tap-changer control mode, shunt / reactive / phase-shifter toggles), reporting time / Newton iters / constrained-line current. Isolates the ~6x reassessment cost to `transformer_voltage_control_on` and shows `AFTER_GENERATOR_VOLTAGE_CONTROL` recovers it. `--contingency` / `--overload` / `--reps`. | `docs/performance/history/reassessment-fast-mode-tap-control.md` |
| `run_all.py` | Drives every benchmark above sequentially. | — |
### `bench_analyze_suggest.py`
```bash
# The reported "this case, first scenario": Pyrenees LANNEL61PRAGN on the
# medium/European grid (its network.xiidm ships as a Git-LFS zip — run
# `git lfs pull` first, or use --tier high for the uncompressed French grid).
python benchmarks/bench_analyze_suggest.py # medium tier, first study
python benchmarks/bench_analyze_suggest.py --tier high # French grid, first study
python benchmarks/bench_analyze_suggest.py --compare # parallel vs serial, same case
```
Two levers this benchmark validates:
- **Per-action reassessment goes serial on a CPU-limited host.** The tail line
reports `reassessment: serial|parallel — N worker(s) / M effective core(s)`.
On a 2-vCPU host the recommender's container-aware detection picks serial;
even on a 4-core dev box `--compare` shows parallel is no faster than serial
(each worker clones a full network), so over-subscribing 2 vCPUs with ~10
workers was the 47 s assessment in the regression.
- **The step-2 result payload no longer ships full-grid per-branch arrays.**
Each combined-action pair used to carry `p_or_combined` / `p_ex_combined`
(one float per line × ~100 pairs ≈ **29 MB** on the European grid); the
frontend reads neither, so they are emptied at the API boundary. Watch
`payload=… KiB` and the `result sanitize_for_json` sub-line drop
(29 269 KiB / 2.57 s → 267 KiB / 0.01 s).
## Reference measurements
On a developer box with pypowsybl 1.14.0 + Python 3.12 + the full
PyPSA-EUR France 400 kV grid, current branch tip:
### `bench_load_study.py`
| Segment | Measured |
|---|---|
| `reset()` | ~0 ms |
| `load_network` | ~2 200 ms |
| `update_config` | ~5 900 ms |
| 4 response XHRs | ~330 ms |
| **Total** | **~8.5 s** |
This maps to ~8.8 s end-to-end wall-clock on Chrome DevTools traces —
see v18 row of `docs/performance/history/loading-parallel.md` (-63 % vs v6 baseline).
### `bench_voltage_level_queries.py`
| Endpoint | Before | After |
|---|---|---|
| `/api/voltage-levels` | 7.5 ms | 4.5 ms |
| `/api/nominal-voltages` | **144 ms** | **5.7 ms** (~25×) |
| `get_monitored_elements` | 265 ms | 175 ms |
| `_get_switches_with_topology` | 174 ms | 141 ms |
### `bench_n1_diagram.py` (contingency `ARGIAL71CANTE`)
| Call | Before | After |
|---|---|---|
| COLD (first view) | 18 125 ms | **4 159 ms** (-77 %) |
| WARM (repeat view) | 11 906 ms | **3 200 ms** (-73 %) |
## When to run them
- **Before pushing a perf patch** on the backend or on
`expert_op4grid_recommender`: run the benchmark closest to the
change and confirm no regression on the rest via `run_all.py`.
- **When a DevTools trace suggests slowdown**: map the hot span to
one of the four scripts to isolate it from web-layer variance.
- **When upstream bumps** `expert_op4grid_recommender`: re-run
`bench_topology_cache.py` + `bench_voltage_level_queries.py` to
catch behavioural changes in pypowsybl / numpy / pandas upgrades.
## Notes
- These scripts import `expert_backend.services.*` directly, so they
need to run inside the Co-Study4Grid venv.
- Each benchmark is idempotent — running one does not alter global
state in a way that would affect the next (each call to
`setup_service` resets the recommender).
- The scripts intentionally use real data paths rather than mocks:
the goal is to measure the full pypowsybl + JNI + pandas stack.
Unit tests in `expert_backend/tests/` cover the mock path.