perf(ds4): accelerate long context and add PFlash - #664
Conversation
There was a problem hiding this comment.
4 issues found across 10 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="server/src/deepseek4/deepseek4_backend.cpp">
<violation number="1" location="server/src/deepseek4/deepseek4_backend.cpp:2648">
P1: When the previous request leaves GPU work in flight, `park()` frees DeepSeek weights and cache before synchronization runs. Synchronize both backends before calling `park()` to prevent queued kernels from accessing freed buffers.</violation>
<violation number="2" location="server/src/deepseek4/deepseek4_backend.cpp:2661">
P2: When `load_drafter()` fails after creating its backend, `pflash_drafter_loaded_` remains false and the context backend is never freed. Free `pflash_drafter_ctx_` on every failed load before restoring the target.</violation>
</file>
<file name="server/src/common/model_capabilities.h">
<violation number="1" location="server/src/common/model_capabilities.h:79">
P2: With a DeepSeek4 `--target-devices` split, this row admits `--prefill-compression`, but the request reaches `DeepSeek4LayerSplitAdapter::compress()`. That inherited default returns `{}`, so `apply_pflash_compression()` returns HTTP 500; gate split placements or implement the adapter path before setting this flag.</violation>
</file>
<file name="server/src/deepseek4/deepseek4_fused_verify.inc">
<violation number="1" location="server/src/deepseek4/deepseek4_fused_verify.inc:545">
P1: When fused verification uses `q > 1` after the SWA ring wraps, this predicate enables sparse attention even though the graph appends preserved raw rows after the compressed rows. The sparse kernel classifies that suffix as compressed and can drop visible preserved rows, so keep sparse attention off for batched lanes or add an explicit preserved-row boundary to the kernel layout.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| if (!load_request->skip_park && !parked_ && | ||
| !park(ParkTarget::TargetModel)) { | ||
| return results; | ||
| } | ||
| if (backend_) ggml_backend_synchronize(backend_); | ||
| if (spec_backend_) ggml_backend_synchronize(spec_backend_); |
There was a problem hiding this comment.
P1: When the previous request leaves GPU work in flight, park() frees DeepSeek weights and cache before synchronization runs. Synchronize both backends before calling park() to prevent queued kernels from accessing freed buffers.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/src/deepseek4/deepseek4_backend.cpp, line 2648:
<comment>When the previous request leaves GPU work in flight, `park()` frees DeepSeek weights and cache before synchronization runs. Synchronize both backends before calling `park()` to prevent queued kernels from accessing freed buffers.</comment>
<file context>
@@ -2599,17 +2607,150 @@ GenerateResult DeepSeek4Backend::restore_and_generate_impl(
+ if (load_request == nullptr) return results;
+
+ const bool was_parked = parked_;
+ if (!load_request->skip_park && !parked_ &&
+ !park(ParkTarget::TargetModel)) {
+ return results;
</file context>
| if (!load_request->skip_park && !parked_ && | |
| !park(ParkTarget::TargetModel)) { | |
| return results; | |
| } | |
| if (backend_) ggml_backend_synchronize(backend_); | |
| if (spec_backend_) ggml_backend_synchronize(spec_backend_); | |
| if (backend_) ggml_backend_synchronize(backend_); | |
| if (spec_backend_) ggml_backend_synchronize(spec_backend_); | |
| if (!load_request->skip_park && !parked_ && | |
| !park(ParkTarget::TargetModel)) { | |
| return results; | |
| } |
| // top-k boundary, preserving fused-graph replay. | ||
| const bool sparse_attention = | ||
| ds4_env_flag("DFLASH_DS4_SPARSE_DECODE_FLASH") && ratio > 0 && | ||
| padded > 2 * w.n_indexer_top_k; |
There was a problem hiding this comment.
P1: When fused verification uses q > 1 after the SWA ring wraps, this predicate enables sparse attention even though the graph appends preserved raw rows after the compressed rows. The sparse kernel classifies that suffix as compressed and can drop visible preserved rows, so keep sparse attention off for batched lanes or add an explicit preserved-row boundary to the kernel layout.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/src/deepseek4/deepseek4_fused_verify.inc, line 545:
<comment>When fused verification uses `q > 1` after the SWA ring wraps, this predicate enables sparse attention even though the graph appends preserved raw rows after the compressed rows. The sparse kernel classifies that suffix as compressed and can drop visible preserved rows, so keep sparse attention off for batched lanes or add an explicit preserved-row boundary to the kernel layout.</comment>
<file context>
@@ -534,13 +534,30 @@ static bool ds4_build_fused_verify_graph(
+ // top-k boundary, preserving fused-graph replay.
+ const bool sparse_attention =
+ ds4_env_flag("DFLASH_DS4_SPARSE_DECODE_FLASH") && ratio > 0 &&
+ padded > 2 * w.n_indexer_top_k;
+ const DeepSeek4AttentionImpl attention_impl = sparse_attention
+ ? DeepSeek4AttentionImpl::SparseFlash
</file context>
| padded > 2 * w.n_indexer_top_k; | |
| padded > 2 * w.n_indexer_top_k && lane_q == 1; |
| if (!load_drafter(load_request->drafter_path, 999, | ||
| load_request->drafter_gpu, | ||
| pflash_drafter_ctx_)) { | ||
| std::fprintf(stderr, "[deepseek4-pflash] load failed: %s\n", | ||
| dflash27b_last_error()); | ||
| if (!load_request->skip_park && !was_parked) { | ||
| unpark(ParkTarget::TargetModel); | ||
| } | ||
| return results; | ||
| } |
There was a problem hiding this comment.
P2: When load_drafter() fails after creating its backend, pflash_drafter_loaded_ remains false and the context backend is never freed. Free pflash_drafter_ctx_ on every failed load before restoring the target.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/src/deepseek4/deepseek4_backend.cpp, line 2661:
<comment>When `load_drafter()` fails after creating its backend, `pflash_drafter_loaded_` remains false and the context backend is never freed. Free `pflash_drafter_ctx_` on every failed load before restoring the target.</comment>
<file context>
@@ -2599,17 +2607,150 @@ GenerateResult DeepSeek4Backend::restore_and_generate_impl(
+ release_pflash_drafter();
+ }
+ if (!pflash_drafter_loaded_) {
+ if (!load_drafter(load_request->drafter_path, 999,
+ load_request->drafter_gpu,
+ pflash_drafter_ctx_)) {
</file context>
| if (!load_drafter(load_request->drafter_path, 999, | |
| load_request->drafter_gpu, | |
| pflash_drafter_ctx_)) { | |
| std::fprintf(stderr, "[deepseek4-pflash] load failed: %s\n", | |
| dflash27b_last_error()); | |
| if (!load_request->skip_park && !was_parked) { | |
| unpark(ParkTarget::TargetModel); | |
| } | |
| return results; | |
| } | |
| if (!load_drafter(load_request->drafter_path, 999, | |
| load_request->drafter_gpu, | |
| pflash_drafter_ctx_)) { | |
| std::fprintf(stderr, "[deepseek4-pflash] load failed: %s\n", | |
| dflash27b_last_error()); | |
| dflash::common::free_drafter(pflash_drafter_ctx_); | |
| if (!load_request->skip_park && !was_parked) { | |
| unpark(ParkTarget::TargetModel); | |
| } | |
| return results; | |
| } |
| {"qwen3", false, false, true, false, kNever, kNever, kNever, kNever, kNever, kNever, kNever}, | ||
| {"gemma4", true, false, false, false, kMono, kNever, kNever, kNever, kBoth, kNever, kNever}, | ||
| {"deepseek4", true, false, false, false, kNever, kNever, kNever, kNever, kNever, kNever, kNever}, | ||
| {"deepseek4", true, false, true, false, kNever, kNever, kNever, kNever, kNever, kNever, kNever}, |
There was a problem hiding this comment.
P2: With a DeepSeek4 --target-devices split, this row admits --prefill-compression, but the request reaches DeepSeek4LayerSplitAdapter::compress(). That inherited default returns {}, so apply_pflash_compression() returns HTTP 500; gate split placements or implement the adapter path before setting this flag.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/src/common/model_capabilities.h, line 79:
<comment>With a DeepSeek4 `--target-devices` split, this row admits `--prefill-compression`, but the request reaches `DeepSeek4LayerSplitAdapter::compress()`. That inherited default returns `{}`, so `apply_pflash_compression()` returns HTTP 500; gate split placements or implement the adapter path before setting this flag.</comment>
<file context>
@@ -76,7 +76,7 @@ inline constexpr ArchCapabilities kArchCapabilities[] = {
{"qwen3", false, false, true, false, kNever, kNever, kNever, kNever, kNever, kNever, kNever},
{"gemma4", true, false, false, false, kMono, kNever, kNever, kNever, kBoth, kNever, kNever},
- {"deepseek4", true, false, false, false, kNever, kNever, kNever, kNever, kNever, kNever, kNever},
+ {"deepseek4", true, false, true, false, kNever, kNever, kNever, kNever, kNever, kNever, kNever},
};
</file context>
Summary
Matched Strix Halo result
PFlash smoke test
Verification