Skip to content

Lower DeepSeek decode inputs to device - #129

Merged
superxf merged 5 commits into
hw-native-sys:mainfrom
high-cloud:agent/deepseek-device-metadata-serving
Jul 31, 2026
Merged

Lower DeepSeek decode inputs to device#129
superxf merged 5 commits into
hw-native-sys:mainfrom
high-cloud:agent/deepseek-device-metadata-serving

Conversation

@high-cloud

@high-cloud high-cloud commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

PyPTO dependency

Includes merged hw-native-sys/pypto-lib#867 through upstream pypto-lib
revision 95206fc.

What changed

  • keep the existing device-side DeepSeek decode metadata path
  • let main and MTP decode consume token IDs without host embedding lookup
  • upload one full BF16 embedding table per decode rank and keep it resident
  • retain the main pre-HC output on device for direct MTP consumption
  • maintain stable per-request device tail-hidden slots for MTP rejection recovery
  • stage only token IDs, positions, accepted counts, tail slot IDs, and cache metadata per decode step
  • keep prefill on the existing host-embedding path

Why

DeepSeek decode still performed host embedding lookup, FP32 x_hc packing, and MTP hidden-window construction after metadata lowering. Those transient hidden tensors added host latency and host-to-device traffic on every step.

The companion PyPTO change performs embedding lookup and hidden packing inside the existing main and MTP decode graphs. No second L2 dispatch is introduced.

The checkpoint has tie_word_embeddings=false, so the embedding table cannot reuse the LM-head weight and is uploaded independently.

Impact

  • removes per-step host embedding and x_hc packing
  • removes the MTP pre-HC hidden round trip through host memory
  • preserves rejection semantics using a stable device tail slot per request
  • preserves host embedding for prefill

Profile

Warm means from the exact 10-token MTP run:

  • decode metadata source preparation: 2.116 ms
  • decode metadata staging: 0.035 ms
  • MTP acceptance: 0.207 ms
  • MTP compact input packing: 0.159 ms
  • main decode L3 dispatch: 38.715 ms
  • MTP decode L3 dispatch: 9.161 ms
  • full decode step: 51.414 ms

The following former host spans are absent:

  • DeepSeekV4ModelRunner.decode.prepare_embeddings
  • DeepSeekV4ModelRunner.decode.pack_x_hc
  • DeepSeekV4ModelRunner.mtp.lookup_embeddings

Validation

  • 86 passed, 2 deselected in the focused DeepSeek/batching suite
  • Python compilation, Ruff, and git diff --check
  • standalone device embedding lookup exact validation
    • task: task_20260730_050705_331978223501
  • 8-card main and MTP decode compilation
    • tasks: task_20260730_050014_300629113280, task_20260730_050224_310317031531
  • 8-card MTP HTTP accuracy
    • task: task_20260730_051813_38056478242
    • exact completion: a leading global information and communications technology (ICT)

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

DeepSeek V4 decode and MTP execution now perform embedding lookup and sampling on device, use grouped cache metadata and persistent device buffers, and remove host hidden-state staging. Executor capability checks, kernel contracts, serving dispatch, resource cleanup, and tests were updated accordingly.

Changes

DeepSeek device-side decode

Layer / File(s) Summary
Device decode capability dispatch
pypto_serving/model/common/executor/executor.py, pypto_serving/model/deepseek/npu_executor.py, pypto_serving/serving/engine/engine.py, pypto_serving/serving/server/serving_worker.py
Adds a dedicated device decode embedding capability and uses it to bypass host embedding lookup where supported.
Decode and MTP kernel contracts
pypto-lib, pypto_serving/model/deepseek/npu_executor.py, pypto_serving/model/deepseek/npu_runner.py
Updates packed arguments and outputs for device embeddings, grouped block counts, accepted-token metadata, tail slots, and sampled IDs.
Device-resident decode and MTP execution
pypto_serving/model/deepseek/npu_runner.py, .gitignore
Stages metadata without host hidden states, manages resident embedding and MTP tail resources, consumes sampled IDs for acceptance, and releases allocations.
Decode and MTP contract validation
tests/test_deepseek_v4.py
Updates ABI, staging, buffer, speculative decoding, sampled-ID, block-count, and tail-slot assertions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Poem

A rabbit hops where hidden states once lay,
While kernels fetch embeddings on their way.
Block counts stack, sampled IDs gleam,
Tail slots persist in a device-side dream.
“Hop forward!” the buffers cheer—
DeepSeek’s new decode path is here!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely summarizes the main change: lowering DeepSeek decode inputs to device.
Description check ✅ Passed The description is detailed and directly explains the device-side embedding, metadata, MTP, performance, and validation changes.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@high-cloud
high-cloud force-pushed the agent/deepseek-device-metadata-serving branch from eff475b to abe01e9 Compare July 30, 2026 07:17
@high-cloud high-cloud changed the title Perf: consume DeepSeek device metadata Lower DeepSeek decode inputs to device Jul 30, 2026
@high-cloud
high-cloud marked this pull request as ready for review July 31, 2026 00:58

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pypto_serving/model/deepseek/npu_runner.py (1)

1965-1975: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

x_hc parameter annotation contradicts the actual argument. Both callers pass x_hc=None and the dataclass field is torch.Tensor | None.

🔧 Proposed fix
-        x_hc: torch.Tensor,
+        x_hc: torch.Tensor | None,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pypto_serving/model/deepseek/npu_runner.py` around lines 1965 - 1975, Update
the x_hc parameter annotation in _prepare_decode_inputs to allow None, matching
both callers and the DeepSeekV4PreparedDecodeInputs dataclass field. Preserve
the existing tensor type for non-null values.
🧹 Nitpick comments (1)
pypto_serving/model/deepseek/npu_runner.py (1)

1630-1646: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer an explicit None check over getattr on a possibly-None state. state is a dataclass that always defines tail_rank/tail_slot_id; the getattr default only exists to tolerate state is None, which the next block already handles explicitly.

♻️ Proposed refactor
             state = self._mtp_request_states.pop(request_id, None)
-            tail_rank = getattr(state, "tail_rank", None)
-            tail_slot_id = getattr(state, "tail_slot_id", None)
-            if tail_rank is not None and tail_slot_id is not None:
-                self._mtp_free_tail_slots[tail_rank].append(tail_slot_id)
-            if state is not None and state.proposed_tokens:
+            if state is None:
+                continue
+            if state.tail_rank is not None and state.tail_slot_id is not None:
+                self._mtp_free_tail_slots[state.tail_rank].append(state.tail_slot_id)
+            if state.proposed_tokens:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pypto_serving/model/deepseek/npu_runner.py` around lines 1630 - 1646, Update
release_finished_requests to access state.tail_rank and state.tail_slot_id only
after explicitly checking that state is not None, while preserving the existing
slot-release and acceptance-logging behavior. Remove the getattr calls and keep
the None guard aligned with the state dataclass’s defined fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pypto_serving/model/deepseek/npu_runner.py`:
- Around line 3932-3937: Replace the ineffective hasattr(self,
"_global_weights") guard in the materialization block with a None check on the
initialized _global_weights value, matching the _inherited_host_weights pattern.
Only call load_packed_global_weights(), _materialize_embedding_device_weight(),
_materialize_main_pre_hc_device(), and the optional MTP materialization when
global weights are present.

---

Outside diff comments:
In `@pypto_serving/model/deepseek/npu_runner.py`:
- Around line 1965-1975: Update the x_hc parameter annotation in
_prepare_decode_inputs to allow None, matching both callers and the
DeepSeekV4PreparedDecodeInputs dataclass field. Preserve the existing tensor
type for non-null values.

---

Nitpick comments:
In `@pypto_serving/model/deepseek/npu_runner.py`:
- Around line 1630-1646: Update release_finished_requests to access
state.tail_rank and state.tail_slot_id only after explicitly checking that state
is not None, while preserving the existing slot-release and acceptance-logging
behavior. Remove the getattr calls and keep the None guard aligned with the
state dataclass’s defined fields.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3c162216-82fc-4fbc-96f3-b2e1cf6fd8de

📥 Commits

Reviewing files that changed from the base of the PR and between 18c7284 and ac4ead4.

📒 Files selected for processing (8)
  • .gitignore
  • pypto-lib
  • pypto_serving/model/common/executor/executor.py
  • pypto_serving/model/deepseek/npu_executor.py
  • pypto_serving/model/deepseek/npu_runner.py
  • pypto_serving/serving/engine/engine.py
  • pypto_serving/serving/server/serving_worker.py
  • tests/test_deepseek_v4.py

Comment thread pypto_serving/model/deepseek/npu_runner.py Outdated
@high-cloud high-cloud closed this Jul 31, 2026
@high-cloud high-cloud reopened this Jul 31, 2026
@superxf
superxf merged commit b7eed12 into hw-native-sys:main Jul 31, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants