Skip to content

[gfx1250] DeepSeek-V4.1: run on gfx1250 (MI455X) with FlyDSL kernels - #2617

Merged
valarLip merged 4 commits into
mainfrom
hyi/v41-gfx1250
Oct 27, 2026
Merged

valarLip merged 4 commits into
mainfrom
hyi/v41-gfx1250

Conversation

@yhl-amd

@yhl-amd yhl-amd commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

DeepSeek-V4.1-Flash starts and serves on gfx1250 (MI455X) at TP4 and DEP4, with its FP8 linears and index scoring on AITER's FlyDSL kernels.

Depends on ROCm/aiter#6863 (FlyDSL paged FP8 MQA logits, merged) and ROCm/aiter#6864 (FlyDSL batched mxfp8_32 GEMM with 32x32 weight scales and K tails).

Run on gfx1250

  • Mono decode's persistent kernels are gfx950 code (wave64, MFMA, permlane32) and fail to compile on gfx1250. Mono's shared deployment checks now refuse any arch but gfx950, so V4.1 (and DSpark's draft and verify) take the unfused path there. The arch is read on use through deployment._gfx(), so the module still imports without aiter.jit; tests pin it to gfx950 (autouse fixture) and one test patches it back.
  • The fixed-order router GEMV is the same gfx950 bf16 MFMA tile; router_supported is gfx950-only, elsewhere the gate's own GEMM routes.

FlyDSL kernels on gfx1250

  • Every FP8 linear with 32x32 weight scales (wqkv_a, wq_b, wo_b, the indexer's wq_b, DSpark's main_proj, Engram's wkv) and the grouped wo_a run on the FlyDSL mxfp8_32 GEMM, which reads the 32x32 scale as stored; a K that 128 does not divide (the shared expert's down projection, K = 576 at TP4) runs as a ragged last k-tile.
  • FP8 index scoring (indexer/score.py) uses flydsl_fp8_paged_mqa_logits on gfx1250, over the same packed, preshuffled 8-row-page cache and with the same arguments as the Triton kernel. This is the shared paged scorer, so any model using it on gfx1250 takes the FlyDSL kernel; other archs are unchanged.

Engram under DP attention (DEP4)

  • With DP attention every rank registers its Engram tables whole, and DEP4 needed ATOM_ENGRAM_UVA=0: a layer's scales and values share a page once widened for cudaHostRegister, and HIP refuses the overlapping registration. Registration now goes through a process-wide, reference-counted set of page-locked runs, and a refused registration clears HIP's last error.
  • Tables are read through a shared read-only mapping of the shard instead of safe_open's private mapping, so registration pins the same page-cache pages for every rank instead of copying each table per rank. The TP path is unchanged.

Docs

recipes/DeepSeek-V4.1-Flash.md gains a gfx1250 (MI455X) section: TP4 / DEP4 launch commands, the differences from gfx950, and GSM8K results.

Test plan

  • Non-GPU unit tests (CI's run_unit_tests.sh on CPU torch): all pass except tests/test_otel_scheduler.py::test_engine_initializes_tracing_before_ready, which also fails on main at 11a2d56.
  • black --check / ruff check clean on the changed files.
  • gfx1250 (MI455X, 4 GPUs): TP4 and DEP4 serve DeepSeek-V4.1-Flash; DEP4 starts without ATOM_ENGRAM_UVA=0. GSM8K 3-shot: TP4 0.9257, TP4 + DSpark5 0.9257 (MI355X 0.9242).

Note: this branch also carries 99908e1 from #2694 (NidhoggD1, unchanged) so the non-GPU unit tests pass; tests/test_otel_scheduler.py::test_engine_initializes_tracing_before_ready fails on main since #2644. It drops out on rebase once #2694 merges.

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

🏷️ CI Guide

Runs automatically on every eligible PR before approval:

  • ✅ Pre Checkin: Black, Ruff, catalog schema validation, non-GPU unit tests

Heavy model tests:

  • ✅ Run after the PR is approved and Pre Checkin passes
  • ✅ Run immediately when an approval review is submitted
  • ✅ Can be requested before approval with labels
Label Tests
ci:full Run all heavy PR model tests: native ATOM, vLLM, and SGLang
ci:atom Run native ATOM model accuracy tests
ci:vllm Run ATOM vLLM OOT model accuracy tests
ci:sglang Run ATOM SGLang model accuracy tests

Heavy jobs are skipped when the PR is not approved and no matching ci:* label is present.
Add labels via the sidebar or gh pr edit 2617 --add-label <label>

DeepSeek-V4.1-Flash now starts and serves on gfx1250 at TP4 and DEP4, with
its FP8 linears and index scoring on AITER's FlyDSL kernels. Requires AITER
#6863 (FlyDSL paged FP8 MQA logits, merged) and #6864 (FlyDSL batched
mxfp8_32 GEMM with 32x32 weight scales and K tails).

Run on gfx1250:
- Mono decode's persistent kernels are gfx950 code (wave64, MFMA,
  permlane32) and fail to compile on gfx1250. Mono's shared deployment checks
  now refuse any arch but gfx950, so V4.1 (and DSpark's draft and verify)
  take the unfused path there. The arch is read on use through
  `deployment._gfx()`, so the module still imports without `aiter.jit`; tests
  pin it to gfx950 (autouse fixture) and one test patches it back.
- The fixed-order router GEMV is the same gfx950 bf16 MFMA tile and fails to
  compile on gfx1250; `router_supported` is gfx950-only, elsewhere the gate's
  own GEMM routes.

FlyDSL kernels on gfx1250:
- Every FP8 linear with 32x32 weight scales (wqkv_a, wq_b, wo_b, the indexer's
  wq_b, DSpark's main_proj, Engram's wkv) and the grouped wo_a run on the
  FlyDSL mxfp8_32 GEMM, which reads the 32x32 scale as stored; a K that 128
  does not divide (the shared expert's down projection, K = 576 at TP4) runs
  as a ragged last k-tile.
- FP8 index scoring (`indexer/score.py`) uses `flydsl_fp8_paged_mqa_logits`
  on gfx1250, over the same packed, preshuffled 8-row-page cache and with the
  same arguments as the Triton kernel. This is the shared paged scorer, so
  any model using it on gfx1250 takes the FlyDSL kernel; other archs are
  unchanged.

Engram under DP attention (DEP4):
- With DP attention every rank's TP group is one GPU, so each rank registers
  its Engram tables whole, and DEP4 needed ATOM_ENGRAM_UVA=0. A layer's
  scales sit right before its values in the shard; each range is widened to
  page boundaries for cudaHostRegister, so the two share a page and HIP
  refuses the overlapping registration. Registration now goes through a
  process-wide, reference-counted set of page-locked runs (a shared page is
  registered once, released with its last user), and a refused registration
  clears HIP's last error instead of failing the next kernel launch.
- safe_open maps a shard privately, so page-locking copied every table page
  into each DP rank. Tables are now read through a shared, read-only mapping
  of the shard, so registration pins the same page-cache pages for every
  rank. The TP path (each rank registers its head shard) is unchanged.

Docs: recipes/DeepSeek-V4.1-Flash.md gains a gfx1250 (MI455X) section with
TP4 / DEP4 launch commands, the differences from gfx950 and GSM8K results.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yhl-amd yhl-amd changed the title [gfx1250] DeepSeek-V4.1: run on gfx1250 [gfx1250] DeepSeek-V4.1: run on gfx1250 (MI455X) with FlyDSL kernels Oct 24, 2026
yhl-amd and others added 2 commits October 26, 2026 11:58
# Conflicts:
#	tests/test_otel_scheduler.py
Resolve engram conflicts in favor of main (#2684): the UVA shard is now
copied into hipHostMalloc memory, so the shared-boundary-page registration
fix (_PageRuns) is no longer needed on the default path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@valarLip
valarLip merged commit f324de4 into main Oct 27, 2026
38 of 41 checks passed
@valarLip
valarLip deleted the hyi/v41-gfx1250 branch October 27, 2026 07:59
PerryZhang01 added a commit to jgong5/ATOM that referenced this pull request Oct 27, 2026
…under nightly 20261026

regen_gpu_gate_baseline.sh on c5ad35a (upstream e893780 merged with
ROCm#2617's head f6a0c13, no Compass; branch
base/upstream-e893780f4-pr2617), mi355-gpu-40 MI355X card 4, aiter
v0.1.27-9-g41d4d8d7e: two runs, 13428 passed and the same 54 failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
valarLip pushed a commit that referenced this pull request Oct 28, 2026
…e UVA path (#2754)

With DP attention every rank's TP group is one GPU, so every rank page-locks
the whole Engram tables (V4.1-Flash: 188.8 GiB of values + scales), the same
tables on every rank of the node. On main (after #2684):

- The default copy (ATOM_ENGRAM_UVA_COPY=1) makes one hipHostMalloc copy per
  rank: 4 x 188.8 GiB = 755 GiB of page-locked memory on a DEP4 node, ~66 s
  of copying per rank, and the page cache evicted to make room.
- The in-place path (ATOM_ENGRAM_UVA_COPY=0) registers each source widened to
  page boundaries. A layer's scales start on the page its values end on, and
  HIP refuses a range overlapping one already registered: every DP rank is
  refused. The refusal is also left as HIP's sticky last error, which torch
  raises at the next kernel launch ("part or all of the requested memory range
  is already mapped"), so the host-path fallback dies too. safe_open maps the
  shard privately, so the registration copies every page on its write fault:
  a whole-table anonymous copy per rank before the refusal.
- A failed hipHostMalloc/hipHostFree also leaves its last error.
- The allocator loads `libamdhip64.so` by name: on a wheel-only install that
  fails and the copy silently becomes an in-place registration; elsewhere it
  can be a second runtime next to the one torch loaded.

Changes (host_memory.py unless noted):

- In-place registrations go through a process-wide, reference-counted set of
  page runs: a range takes a reference on the runs covering it and registers
  only the gaps; a run is unregistered with its last lease.
- Every refused or failed HIP call (hipHostRegister, hipHostUnregister,
  hipHostMalloc, hipHostFree) clears HIP's last error.
- The HIP runtime is the one torch loaded (found in /proc/self/maps, then
  torch/lib, then the soname), logged once; without one the shard is
  registered in place, as before.
- weights.py: the Engram tables are read through a shared read-only mapping
  of the shard (`shared_tensor`, MAP_SHARED + PROT_READ), not safe_open's
  private one. A registration pins the page cache itself, the same pages for
  every process; #2684's `_source_dropper` works on it unchanged (clean file
  pages). This replaces #2617's `CheckpointReader._shared_tensor` (same
  mapping), so there is one helper.
- A read-only range is faulted in on several threads (MADV_POPULATE_READ)
  before it is registered, and registered with hipHostRegisterReadOnly.
- host.py: under DP attention (TP group of one, DP group > 1) the tables are
  `shared`, and `pin_rows` registers them in place unless
  ATOM_ENGRAM_UVA_COPY is set; a refused registration falls back to the copy.
  An explicit ATOM_ENGRAM_UVA_COPY keeps its meaning for every rank, and a
  TP rank (its own head shard) still copies by default.
- bench_uva_shard.py: `--dp N` (whole tables, shared), the server's mapping,
  and a check of the gathered rows against the host path.

Measured on gfx1250 (MI455X, 4 processes each page-locking the whole V4.1-Flash
tables, warm page cache): main copies 4 x 188.8 GiB (Shmem +755 GiB, MemFree
down to 4 GiB) in 66.5 s per rank; with this change each rank registers in
place in 1.04 s, no new memory (each rank's Pss 48 GiB = one shared copy / 4),
and the UVA kernel's rows match the host path. main with COPY=0: all four
ranks refused, after 354 GiB of anonymous copy-on-write pages. A DEP4 server
(main, which now includes #2617) reaches the same point with node Shmem peaking at 16 GiB
instead of 771 GiB, and the Engram stage takes ~10 s instead of ~30 s.

Tests: overlapping and adjacent ranges, reference-counted release, refusal
rollback, last-error clearing (register, malloc, free), the loaded runtime,
the copy policy (env unset/0/1 x shared or not), refused-shared fallback, the
shared read-only mapping (in place read-only; copied with the dropper), the DP
branch selection in EngramHost, and on the GPU: adjacent tables, host path
after a refusal and after a failed malloc, the runtime torch loaded, and the
UVA kernel reading a shared read-only mapping in place and copied.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
PerryZhang01 added a commit to jgong5/ATOM that referenced this pull request Oct 28, 2026
…6a0c13 (#2340)

Merge upstream ROCm/ATOM e893780 (nightly 20261026) and ROCm#2617's head
f6a0c13, which runs DeepSeek-V4.1 on gfx1250, into feature/atomcompass_new as one
merge commit; every conflict keeps both sides.

Compass follows upstream where the merge broke it: the aiter stand-ins no longer
delete a real aiter under upstream's tests/aiter_stub.py, a simulated run accepts
the API server's tracing executor, tests/conftest.py records the session's own
KFD entry before its imports open /dev/kfd, and the module walk reads
DSparkMarkovHead where upstream moved it. The CPU gate runs on
rocm/atom-dev:vllm-v0.31.0-nightly_20261026.

Each machine spec keeps aiter 9205e1059, the stack its readings came from, so
compass measure memory refuses nightly 20261026 until #2440 re-reads the specs
and refits the laws; the five laws are re-keyed to the merged atom digest only.

Closes #2140.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants