multi-repo-dsv4flash-setup · git:20260721.f5e17fb · 2026-07-21 · sha256 b584dbff9c6fed77
multi-repo-dsv4flash-setup git:20260721.f5e17fbA
Immutable. This exact content is served forever at /api/v1/blob/b584dbff9c6fed77.
---
name: multi-repo-dsv4flash-setup
description: Concrete guide to benchmark DeepSeek-V4 (dsv4) FLASH on NPU against the current worktree's simpler — the three decode attentions (swa/csa/hca) and the distributed MoE, all in pypto-lib's models/deepseek/v4/. Defers cross-repo clone/install to multi-repo-setup, then gives the exact PYPTO_BENCH run commands and the hard-won config that makes the numbers reproducible: pin ptoas 0.48 (0.50/ptoas-bin cannot compile csa), pin --start-pos 8192, edit moe.py for 16 experts/card, read fast_effective_us not effective_us, pin an even die, and apply the eager-init idempotency guard so distributed MoE runs on newest simpler. Invoke when benchmarking dsv4 flash attention or MoE, reproducing a dsv4 effective-time number, or hitting "add_worker after init" / a csa compile timeout / wandering attention numbers.
---
# DeepSeek-V4 FLASH on NPU: attention + MoE effective-time benchmark
This skill is the **dsv4-flash-specific concrete guide**. It benchmarks the
four kernels that live in **pypto-lib** at `models/deepseek/v4/` against the
current worktree's simpler:
| kernel | file | measures | dies |
| ------ | ---- | -------- | ---- |
| SWA attention | `decode_attention_swa.py` | decode attn effective_us | 1 |
| CSA attention | `decode_attention_csa.py` | decode attn effective_us | 1 |
| HCA attention | `decode_attention_hca.py` | decode attn effective_us | 1 |
| MoE (distributed) | `moe.py` | decode MoE fast_effective_us | ≥2 (EP) |
It sits on top of [`multi-repo-setup`](../multi-repo-setup/SKILL.md), which
owns the generic repo-graph + clone + install steps this skill does not
repeat. Everything below is the dsv4-specific run recipe and the five
configuration facts that make the effective-time numbers **reproducible to
~1%** instead of wandering ±18% run-to-run.
## 1. Setup — defer to multi-repo-setup
Run [`multi-repo-setup`](../multi-repo-setup/SKILL.md) first — it clones
pto-isa / pypto / pypto-lib under `build/`, exports the toolchain env, and
installs the simpler you want (worktree or main). Then, from the worktree:
```bash
source .venv/bin/activate
eval "$(pypto-setup --export)" # ASCEND_HOME_PATH, gcc, PATH
```
Verify the loaded simpler is the worktree's, not a user-site shadow:
`python -c "import simpler; print(simpler.__file__)"`.
## 2. The benchmark env: PYPTO_BENCH and its two metrics
`PYPTO_BENCH=1` (a pypto-lib golden/runner gate) times each kernel over 100
rounds (5 warmup) and prints:
- **`effective_us`** — the on-device effective time. For the **single-card
attentions** this is *the* number to read (min/median/mean/max over 100
rounds).
- **`fast_effective_us`** — for **distributed MoE only**: each round
contributes the fastest valid rank's Effective time. This is the **clean,
stable** MoE metric (valid_rounds should be 100/100). The MoE
`effective_us` (all-rank) carries a long contention tail — median wobbles
and max can spike 20× — so **quote `fast_effective_us` for MoE**, never the
all-rank `effective_us`.
## 3. Five configuration facts — get these right or the numbers lie
These were each established empirically; skipping any one makes a run
non-comparable.
### 3.1 ptoas 0.48 — csa cannot compile on 0.50 / default ptoas-bin
`decode_attention_csa`'s `csa_slots_build_valid_qk_plan` does **not** compile
within the 60 s ptoas timeout on **ptoas 0.50** or the default
`/usr/local/bin/ptoas-bin`. Versions **0.45–0.48 compile it in ~4 s**.
Pin 0.48 explicitly — and note `pypto-setup --export` sets PATH to the slow
default, so pinning must come **after** the export:
```bash
eval "$(pypto-setup --export)"
export PTOAS_ROOT=/usr/local/ptoas/0.48
export PATH="/usr/local/ptoas/0.48/bin:$PATH" # must win over pypto-setup's default
```
(`task-submit --ptoas 0.48` also selects it, but `pypto-setup --export` inside
the `--run` body overrides that — so set `PTOAS_ROOT`/`PATH` in the driver.)
swa and hca compile on any version; only csa is version-sensitive.
### 3.2 pto-isa pinned commit
Keep pto-isa at the commit the worktree expects
(`SIMPLER_PTO_ISA_COMMIT`, e.g. `83d01313`) with `PTO_ISA_ROOT` pointing at
the clone. A different pto-isa changes tile codegen and the numbers.
### 3.3 Attention: pin `--start-pos 8192`
The attention scripts take `--start-pos` (default `None` → a *mixed*
canonical fixture, `swa_decode_start_set`: sliding-window regimes + 8k). The
default is **not** a fixed workload — HCA in particular swings (~353 µs on the
mixed default vs ~270 µs at uniform 8192) because the KV length changes. For a
comparable long-context number, force **`--start-pos 8192`** on all three.
SWA/CSA move less but pin it anyway for a single canonical config.
### 3.4 MoE: experts-per-card via the `moe.py` divisor
`moe.py:36` computes global experts from a divisor; **per-card experts
(`N_LOCAL`) = 256 // divisor**, independent of `--ep`:
```python
# base config.FLASH.n_routed_experts == 256
config.FLASH = dataclasses.replace(config.FLASH,
n_routed_experts=config.FLASH.n_routed_experts // 16 * EP) # // 8 → 32/card, // 16 → 16/card
```
| divisor | per-card experts (`N_LOCAL`) |
| ------- | ---------------------------- |
| `// 8` (repo default = EP8/32-per-card) | 32 |
| `// 16` | **16** |
For **16 experts/card**, edit `// 8` → `// 16`, then run `--ep 2` (EP2, 2
dies). `--ep {2,4,8}` picks the EP world size; `N_LOCAL` stays 16 regardless
(EP8 → 128 global / 8 cards). This is a benchmark-only edit — do not commit it.
### 3.5 Eager-init adaptation — else MoE dies with `add_worker after init`
Newest simpler (eager-init, upstream #1397) makes `Worker.init()` eagerly run
the hierarchical start; the C++ `add_worker` then throws
`Worker: add_worker after init` when pypto's `DistributedWorker.__init__`
calls `_start_hierarchical()` a second time to force an eager pre-fork
(pypto's own comment calls that call "idempotent"). Newest simpler broke that
idempotency. **Restore it** with a guard at the top of
`_start_hierarchical` in `python/simpler/worker.py`:
```python
# Idempotent once init() has driven the one hierarchical start
# (state == "started"): a repeat call is a no-op — the C++ Worker is
# already inited and re-running add_worker would throw
# "add_worker after init". A distributed runner may call this again to
# force an eager pre-fork; that fork already happened inside init().
if self._hierarchical_start_state == "started" or getattr(self, "_hierarchical_started", False):
return
```
After editing simpler python, reinstall editable
(`pip install --no-build-isolation -e .`) so the change is picked up. This is
a genuine eager-init ↔ distributed compatibility fix, not a downgrade — it
belongs upstream; land it rather than carrying it locally forever.
## 4. Run it — onboard, pinned, reproducible
Onboard rules apply: hold an exclusive die via `task-submit` and gate on
[`onboard-arch-precheck`](../onboard-arch-precheck/SKILL.md). **Pin an even
die** — `--device auto` picks a different die each run and neighbor HBM
contention on a shared package swings memory-bound decode kernels ±18%.
Pinning die + start_pos + ptoas reproduces to ~1%.
```bash
.claude/skills/onboard-arch-precheck/check.sh a2a3 || exit 1
SKILL=.claude/skills/multi-repo-dsv4flash-setup
# Three attentions on one pinned even die (start_pos=8192, ptoas 0.48):
task-submit --timeout 1200 --device 0 --run "$SKILL/bench_attn.sh $PWD 0"
# MoE 16-experts/card, EP2, on two pinned even dies (edit moe.py //16 first):
task-submit --timeout 900 --device 2,4 --run "$SKILL/bench_moe.sh $PWD 2,4"
```
Both drivers set `PTOAS_ROOT=0.48`, `PYPTO_BENCH=1`, and the pto-isa pin, then
run the kernels and grep the effective lines. Read `bench_attn.sh` /
`bench_moe.sh` for the exact env; pass the simpler-worktree root as `$1` and
the device(s) as `$2`.
## 5. Canonical baseline (a2a3, ptoas 0.48, start_pos=8192, PYPTO_BENCH)
Quiet single pinned die; expect ~1% run-to-run at this config:
| kernel | metric | µs (median) |
| ------ | ------ | ----------: |
| decode_attention_swa | effective_us | ~246 |
| decode_attention_csa | effective_us | ~383 |
| decode_attention_hca | effective_us | ~270 |
| MoE (16/card, EP2) | fast_effective_us | ~465–490 |
If a run lands well outside these, check the five facts in §3 **before**
suspecting a regression — a mismatched die/ptoas/start_pos explains almost
every discrepancy.
## Anti-patterns
- ❌ Reading MoE `effective_us` (all-rank) as the headline — it has a
contention tail (median wanders, max spikes 20×). Use `fast_effective_us`.
- ❌ Comparing attention numbers across runs on different dies via
`--device auto` — ±18% die/contention noise. Pin an even die.
- ❌ Comparing attention numbers without fixing `--start-pos` — the default
fixture is a mixed set; HCA alone moves ~30% between the default and 8192.
- ❌ Letting `pypto-setup --export` leave ptoas at the default `ptoas-bin` —
csa then times out at 60 s and looks like a codegen bug. Pin 0.48 after the
export.
- ❌ Reading `add_worker after init` on MoE as a simpler bug — it's the
eager-init ↔ distributed idempotency gap (§3.5). Apply the guard.
- ❌ Committing the `moe.py` `// 16` edit — it's a benchmark-only knob.