项目文件夹

文件
wehub-resource-sync 2114b14ee0
Sync main into demo / sync (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:26 +08:00

100 行
4.8 KiB
Markdown

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# Known Issues
Silent traps and host limits that affect `bench_env` at production scale. Each issue here produces wrong numbers or hangs *without* crashing, so it is worth scanning this page once before configuring a large run.
---
## 1. `--processes N --isolation contexts` silently lowers SR
**Symptom.** No crash, no error logs. SR drops by roughly 4–5 percentage points compared to an equivalent `--isolation pages` run. The regression concentrates on tasks that inject seed state in `_post_sample`.
**Mechanism.** Under multi-process sharding, all N contexts inside a single Chromium begin `reset` simultaneously through `asyncio.gather` with no stagger. IndexedDB hydrate completes *after* `_post_sample` has called `setState`, and silently overwrites the injected seed values. The race window does not fire on `--isolation pages` (pages share an IDB origin so hydrate is already warm when the second page resets) and does not fire on single-process `contexts` (natural ramp staggering keeps the window narrow).
**Workaround.** Use `--isolation pages` for all multi-process runs. It is the production default and is faster than `contexts` in practice.
---
## 2. Page-per-browser limit: keep `N / B ≤ 8`
**Symptom.** Beyond roughly 8 pages in one Chromium process: occasional pointer-event jitter, short GC pauses, and flaky page initialization. Not a hard failure, but stability degrades.
**Mechanism.** A single Chromium process serializes parts of its rendering pipeline (renderer GC, main-thread compositor) across all its pages. Empirically, 6–8 concurrent pages per browser is the cleanly-handled range.
**Workaround.** With `--isolation pages`, configure `--browsers B --parallel N` so that `N / B ≤ 8`. To grow total concurrency, add browsers — not pages per browser. For best fault isolation, **pair browsers and processes 1:1** (`--processes B --browsers B`): each Chromium then runs under its own Python worker, so a single browser crash or memory leak is contained to one shard and cannot disrupt the rest.
Recommended layouts (all `--isolation pages`):
| Target parallelism | Layout |
|---|---|
| ≤ 8 | 1 process × 1 browser × N pages |
| 16 | 2 processes × 1 browser × 8 pages |
| 32 | 4 processes × 1 browser × 8 pages |
| 256 | 32 processes × 1 browser × 8 pages |
---
## 3. `--parallel ≥ 192` stalls on `_wait_ready __SIM__ timeout`
**Symptom.** `errors.jsonl` fills with entries like
```
RuntimeError: [WN][page#1] _wait_ready phase=__SIM__ timeout:
TimeoutError: Page.wait_for_function: Timeout 60000ms exceeded.
```
while CPU, GPU, network, and disk all sit around 10% idle. The pipeline looks starved but nothing is actually busy.
**Mechanism.** Linux caps `fs.inotify.max_user_instances` per uid (default 128 on older kernels, 1024 on Ubuntu 22.04+). Each headless Chromium creates at least one inotify instance. Once the per-uid cap is reached, `inotify_init()` returns `EMFILE`, and the affected Chromium subsystems enter silent retry loops — deferring `__SIM__` exposure past the 60-second readiness timeout.
### Diagnostic
While the run is stalling, in a separate shell:
```bash
find /proc/*/fd -lname 'anon_inode:inotify' 2>/dev/null | wc -l
```
- The number ≈ `cat /proc/sys/fs/inotify/max_user_instances` → this is the cause.
- The number ≪ the cap → look elsewhere.
An idle system shows fewer than 50 inotify instances.
### Workaround (with sudo, preferred)
```bash
# Temporary (resets on reboot)
sudo sysctl -w fs.inotify.max_user_instances=8192
# Persistent
echo "fs.inotify.max_user_instances = 8192" | sudo tee /etc/sysctl.d/99-mobilegym.conf
sudo sysctl --system
```
8192 is conservative; ML and CI hosts commonly run 32 768 – 524 288. The value only sets a kernel hash-table preallocation cap and has no security implications.
### Workaround (no sudo)
The inotify cap is per uid, so two alternatives exist:
1. **Run on a different uid.** Two users sharing the host each get their own 128 instances.
2. **Stagger the launch.** Split one large run into chunks of ≤ 80 envs each, launched at least 60 s apart. Inotify usage stabilizes between chunks. Merge `results.jsonl` and `summary.json` after all chunks complete.
```bash
python -m bench_env.run --parallel 80 --runs-dir runs/part1 ... &
sleep 60
python -m bench_env.run --parallel 80 --runs-dir runs/part2 ... &
sleep 60
python -m bench_env.run --parallel 80 --runs-dir runs/part3 ... &
wait
```
### What does *not* work
User namespaces cannot bypass this limit. Although `unshare --user --map-root-user` grants `CAP_SYS_RESOURCE` inside the new namespace, modifying `fs.inotify.max_user_instances` requires the capability in the init namespace; child namespaces inherit the parent's cap and can only lower it.
---
## See also
- [`FRAMEWORK.md`](FRAMEWORK.md) §6 — full isolation-level reference and multi-process sharding semantics.