Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,14 @@ jobs:
test_dflash test_generate test_flash_attn_sparse test_server_unit \
test_deepseek4_unit test_feature_gate test_seq_slot_manager \
test_seq_engine_contract test_seq_batch_plan test_client_send_buffer \
test_deepseek4_page_layout test_deepseek4_paged_cache \
test_model_smoke test_batched_gdn test_concat_transpose -j$(nproc)

- name: Run C++ server unit tests
run: |
cd server/build
ctest --output-on-failure \
-R "server_unit|deepseek4_unit|feature_gate|seq_slot_manager|seq_engine_contract|seq_batch_plan|client_send_buffer|batched_gdn_cpu" \
-R "server_unit|deepseek4_unit|feature_gate|seq_slot_manager|seq_engine_contract|seq_batch_plan|client_send_buffer|deepseek4_page_layout|deepseek4_paged_cache|batched_gdn_cpu" \
--no-tests=error

- name: Populate venv with cu128 torch + setuptools
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,9 @@ When compression is on, multi-turn continuations automatically use **FlowKV**: a
| `DFLASH_PREFILL_CACHE_SLOTS=N` | `0` | Container-entrypoint equivalent of `--prefill-cache-slots`; the native binary itself uses the CLI flag. |
| `--kv-cache-dir <path>` | — | Persist prefix cache to disk |
| `--kv-cache-budget N` | — | On-disk cache size cap |
| `--paged-attention` | off | Exact 16-token block-table attention for Qwen3.6-27B; see [paged attention](optimizations/paged_attention/README.md) |
| `--max-concurrency N` | `1` | Maximum concurrent sequence slots. Values 2–64 enable paged attention automatically. |
| `--kv-pool-tokens N` | `0` (auto) | Shared physical K/V capacity for concurrent paged serving. Requires `--max-concurrency` greater than 1. Zero derives capacity from available device memory; explicit values are rounded to whole 16-token blocks. |
| `--paged-attention` | off | Exact block-table attention for monolithic Qwen3.6-27B (16-token blocks) and DeepSeek4 on Strix Halo or R9700 + Strix Halo (128-token pages); see [paged attention](optimizations/paged_attention/README.md) and [DeepSeek4 concurrent serving](server/docs/DS4.md#strix-halo-concurrent-serving) |
| `--max-concurrency N` | `1` | Maximum concurrent sequence slots. Values above 1 enable paged attention automatically; Qwen supports up to 64, while DeepSeek4 supports up to 6 on either monolithic Strix Halo or heterogeneous R9700 + Strix Halo. |
| `--kv-pool-tokens N` | `0` (auto) | Shared physical K/V capacity for concurrent paged serving. Requires `--max-concurrency` greater than 1. Zero derives capacity from available device memory; explicit values are rounded to the backend's page size. |
| `--admission-coalesce-ms N` | `20` | Idle-to-busy batching window for concurrent serving, from 0 to 1000 ms; `0` disables it. |

**Bounded KV residency (KVFlash)**
Expand Down
24 changes: 22 additions & 2 deletions server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,8 @@ add_library(dflash_common STATIC
src/deepseek4/deepseek4_loader.cpp
src/deepseek4/deepseek4_graph.cpp
src/deepseek4/deepseek4_roctx.cpp
src/deepseek4/deepseek4_paged_cache.cpp
src/deepseek4/deepseek4_seq_engine.cpp
src/deepseek4/deepseek4_backend.cpp
src/deepseek4/deepseek4_daemon.cpp
src/deepseek4/deepseek4_layer_split_adapter.cpp
Expand Down Expand Up @@ -466,7 +468,7 @@ add_library(dflash_common STATIC
src/common/dflash_draft_kv.cpp
src/common/dflash_spec_decode.cpp
src/common/concurrency/paged_kv_pool.cpp
src/qwen35/concurrency/qwen35_slot_manager.cpp
src/common/concurrency/seq_slot_manager.cpp
src/common/layer_split_backend.cpp
src/common/layer_split_runtime.cpp
src/qwen35/graph_builders.cpp
Expand Down Expand Up @@ -1417,11 +1419,29 @@ if(DFLASH27B_TESTS)
${CMAKE_CURRENT_SOURCE_DIR}/src)
list(APPEND _raw_unit_test_targets test_paged_kv_pool)
endif()
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/test_deepseek4_page_layout.cpp")
add_executable(test_deepseek4_page_layout
test/test_deepseek4_page_layout.cpp)
target_include_directories(test_deepseek4_page_layout PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/include)
list(APPEND _raw_unit_test_targets test_deepseek4_page_layout)
endif()
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/test_deepseek4_paged_cache.cpp")
add_executable(test_deepseek4_paged_cache
test/test_deepseek4_paged_cache.cpp
src/deepseek4/deepseek4_paged_cache.cpp)
target_compile_definitions(test_deepseek4_paged_cache PRIVATE
DFLASH_DS4_PLAN_ONLY=1)
target_include_directories(test_deepseek4_paged_cache PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src)
list(APPEND _raw_unit_test_targets test_deepseek4_paged_cache)
endif()
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/test_seq_slot_manager.cpp")
# Host-side slot bookkeeping test (concurrent serving): no GPU.
add_executable(test_seq_slot_manager
test/test_seq_slot_manager.cpp
src/qwen35/concurrency/qwen35_slot_manager.cpp
src/common/concurrency/seq_slot_manager.cpp
src/common/concurrency/paged_kv_pool.cpp)
target_include_directories(test_seq_slot_manager PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src)
Expand Down
78 changes: 78 additions & 0 deletions server/docs/DS4.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,84 @@ performance profile held 48.1 tok/s median on the deterministic 128-token
workload. The all-6-expert reference-exact mode is a correctness profile, not
a throughput profile.

### Strix Halo concurrent serving

DeepSeek4 paged concurrency supports two resident HIP deployments:

- one monolithic Strix Halo (`gfx1151`) target with every expert on that
device, through 6 lanes; and
- the in-process R9700 (`gfx1201`) target + Strix Halo expert-parallel
deployment, through 6 lanes. The target keeps dense work and its selected
experts while the secondary owns the remaining materialized experts.

The heterogeneous mode is route-level expert parallelism. It is not an
explicit `--target-device hip:0,hip:1` layer split and does not use a remote
target shard or host-streamed experts.

The backend keeps raw MLA rows, compressed rows, indexer state, sequence
lengths, and block tables in a persistent 128-token paged cache. The shared
HTTP scheduler performs admission, cancellation, slow-client isolation, and
fair continuous batching. DeepSeek4 lowers each scheduler plan into one exact
gathered graph with up to 6 independent lanes. Decode rows share the weight
pass; each selected prompt advances by one exact token because the graph must
not contain two rows from the same sequence.

```bash
hf download Lucebox/DeepSeek-V4-Flash-0731-ROCmFP3 \
DeepSeek-V4-Flash-0731-ROCMFPX-MIX-STRIX.gguf \
--local-dir /path/to/models

cmake -S server -B server/build-hip \
-DDFLASH27B_GPU_BACKEND=hip \
-DDFLASH27B_HIP_ARCHITECTURES=gfx1151 \
-DDFLASH27B_SERVER=ON
cmake --build server/build-hip -j

./server/build-hip/dflash_server \
/path/to/models/DeepSeek-V4-Flash-0731-ROCMFPX-MIX-STRIX.gguf \
--target-device hip:0 \
--paged-attention \
--max-concurrency 6 \
--kv-pool-tokens 24576 \
--max-ctx 4096 \
--ds4-prefill exact \
--prefix-cache-slots 0
```

For the R9700 + Strix Halo path, build one HIP binary for both architectures as
described above, expose the R9700 first, and select the static in-process expert
split:

```bash
export HIP_VISIBLE_DEVICES=<r9700-index>,<strix-index>
export DFLASH_DS4_MOE_TP=1
export DFLASH_DS4_MOE_TP_INPROC=1
export DFLASH_DS4_MOE_TP_GPU=1
export DFLASH_EXPERT_BUDGET_MB=11700

./server/build-hip-dual/dflash_server \
/path/to/models/DeepSeek-V4-Flash.gguf \
--target-device hip:0 \
--peer-access \
--paged-attention \
--max-concurrency 6 \
--kv-pool-tokens 24576 \
--max-ctx 4096 \
--ds4-prefill exact \
--prefix-cache-slots 0
```

The heterogeneous paged cache and its full-context prefill staging allocation
are charged against the R9700 before selecting resident experts. Increase
`--kv-pool-tokens` only when the primary has enough memory for the larger
shared history pool.

Paged concurrency fails closed for other primary/secondary architecture pairs,
CUDA or out-of-process expert ownership, explicit layer or remote target
splits, drafts/DSpark, DDTree, PFlash/KVFlash, fused decode, approximate
prefill, windowed attention, mutable expert caching, and prefix-cache parking.
There is no automatic fallback to a slower or asymmetric execution mode.

### Local single-shard

If the adapter decides all 43 layers fit on one CUDA GPU, it loads a single shard locally and no IPC daemon is involved.
Expand Down
4 changes: 2 additions & 2 deletions server/src/common/backend_args.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,12 @@ struct BackendArgs {
// Attention and speculative-decode options. Individual backends consume
// only the fields they support.
int fa_window = 0; // 0 = full attention. qwen3.6 full-attn layers must see the whole context; a finite window drops the system prompt/tools -> breaks tool calls.
bool paged_attention = false; // 16-token paged K/V blocks for AR decode
bool paged_attention = false; // model-specific paged K/V blocks for AR decode
// Concurrent decode slots (--max-concurrency). > 1 requires paged_attention;
// the backend serves that many sequences through the seq_* slot API.
int max_concurrency = 1;
// Total paged K/V pool in tokens shared by all slots (--kv-pool-tokens;
// block-rounded). 0 = derive capacity from available device memory.
// model-page-rounded). 0 = derive capacity from available device memory.
long long kv_pool_tokens = 0;
int kq_stride_pad = 32;
int draft_block_size = 0; // 0 = drafter metadata
Expand Down
6 changes: 6 additions & 0 deletions server/src/common/backend_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ DFLASH_CHECK_ARCH("deepseek4", DeepSeek4BackendConfig, DeepSeek4LayerSplitAdapte
// that shared struct would fail a check that is really about dispatch.
DFLASH_CHECK_ARCH_OPTION("qwen35", Qwen35Config, Qwen35LayerSplitAdapterConfig,
has_paged_attention, paged_attn);
DFLASH_CHECK_ARCH_OPTION("deepseek4", DeepSeek4BackendConfig,
DeepSeek4LayerSplitAdapterConfig,
has_paged_attention, paged_attn);
DFLASH_CHECK_ARCH_OPTION("qwen35", Qwen35Config, Qwen35LayerSplitAdapterConfig,
has_draft_block_size, draft_block_size);

Expand Down Expand Up @@ -430,6 +433,9 @@ std::unique_ptr<ModelBackend> create_backend(
cfg.fused_decode = args.ds4_fused_decode;
cfg.fused_verify_f16_kv = args.ds4_fused_verify_f16_kv;
cfg.prefill_mode = args.ds4_prefill_mode;
cfg.paged_attention = args.paged_attention;
cfg.max_concurrency = args.max_concurrency;
cfg.kv_pool_tokens = args.kv_pool_tokens;

auto backend = std::make_unique<DeepSeek4Backend>(cfg);
if (!backend->init()) {
Expand Down
Loading
Loading