-
Notifications
You must be signed in to change notification settings - Fork 210
perf(m3): enable prefill CUDA graph + EAGLE3 draft-loop kernel cleanup#837
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
09197b2
22aaf8b
51701c6
50c15c5
96c0c78
e3088de
f4e9e01
1ee72e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,15 +40,18 @@ | |
| from tokenspeed.runtime.execution.forward_batch_info import ForwardMode | ||
| from tokenspeed.runtime.layers.activation import SiluAndMul | ||
| from tokenspeed.runtime.layers.common import concat | ||
| from tokenspeed.runtime.layers.layernorm import RMSNorm | ||
| from tokenspeed.runtime.layers.layernorm import FusedRMSNorm, RMSNorm | ||
| from tokenspeed.runtime.layers.linear import ( | ||
| ColumnParallelLinear, | ||
| MergedColumnParallelLinear, | ||
| RowParallelLinear, | ||
| ) | ||
| from tokenspeed.runtime.layers.logits_processor import LogitsProcessor | ||
| from tokenspeed.runtime.layers.quantization.base_config import QuantizationConfig | ||
| from tokenspeed.runtime.layers.vocab_parallel_embedding import ParallelLMHead | ||
| from tokenspeed.runtime.layers.vocab_parallel_embedding import ( | ||
| ParallelLMHead, | ||
| VocabParallelEmbedding, | ||
| ) | ||
| from tokenspeed.runtime.model_loader.weight_utils import default_weight_loader | ||
| from tokenspeed.runtime.models.base import ( | ||
| BaseCausalLM, | ||
|
|
@@ -230,6 +233,10 @@ def __init__( | |
| ) | ||
|
|
||
| self.hidden_norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps) | ||
| self.fused_input_hidden_norm = FusedRMSNorm( | ||
| self.input_layernorm, | ||
| self.hidden_norm, | ||
| ) | ||
|
|
||
| def resolve_attn(self, prefix: str) -> nn.Module: | ||
|
|
||
|
|
@@ -295,11 +302,27 @@ def forward_low_latency( | |
| embeds, | ||
| torch.zeros_like(embeds), | ||
| ) | ||
| hidden_states = self.hidden_norm(hidden_states) | ||
| hidden_states = concat(embeds, hidden_states) | ||
| else: | ||
| embeds = self.input_layernorm(embeds) | ||
|
|
||
| hidden_states = self.hidden_norm(hidden_states) | ||
| hidden_states = concat(embeds, hidden_states) | ||
| h = embeds.size(-1) | ||
| fused_norm_out = torch.empty( | ||
| embeds.size(0), | ||
| h + hidden_states.size(-1), | ||
| dtype=embeds.dtype, | ||
| device=embeds.device, | ||
| ) | ||
| # FusedRMSNorm's q_a/kv_a kwargs are MLA-specific names. Here | ||
| # embeds and hidden_states correspond to q_a and kv_a, separately. | ||
| # Skip the launch on zero-token (idle) forwards. | ||
| if embeds.shape[0] > 0: | ||
| self.fused_input_hidden_norm( | ||
| input_q_a=embeds, | ||
| input_kv_a=hidden_states, | ||
| output_q_a=fused_norm_out[..., :h], | ||
| output_kv_a=fused_norm_out[..., h:], | ||
| ) | ||
| hidden_states = fused_norm_out | ||
|
|
||
| # Attention | ||
| hidden_states = self.comm_manager.pre_attn_comm(hidden_states, ctx) | ||
|
|
@@ -363,9 +386,24 @@ def forward( | |
| # Non-fused path: fuse_embed_reduce is always False here because | ||
| # the model only sets it when should_fuse() is True. | ||
| residual = hidden_states | ||
| embeds = self.input_layernorm(embeds) | ||
| hidden_states = self.hidden_norm(hidden_states) | ||
| hidden_states = torch.cat([embeds, hidden_states], dim=-1) | ||
| h = embeds.size(-1) | ||
| fused_norm_out = torch.empty( | ||
| embeds.size(0), | ||
| h + hidden_states.size(-1), | ||
| dtype=embeds.dtype, | ||
| device=embeds.device, | ||
| ) | ||
| # FusedRMSNorm's q_a/kv_a kwargs are MLA-specific names. Here | ||
| # embeds and hidden_states correspond to q_a and kv_a, separately. | ||
| # Skip the launch on zero-token (idle) forwards. | ||
| if embeds.shape[0] > 0: | ||
| self.fused_input_hidden_norm( | ||
| input_q_a=embeds, | ||
| input_kv_a=hidden_states, | ||
| output_q_a=fused_norm_out[..., :h], | ||
| output_kv_a=fused_norm_out[..., h:], | ||
| ) | ||
| hidden_states = fused_norm_out | ||
|
|
||
| # Attention | ||
| hidden_states = self.comm_manager.pre_attn_comm(hidden_states, ctx) | ||
|
|
@@ -458,6 +496,16 @@ def __init__( | |
| if getattr(config, "fc_norm", False) | ||
| else None | ||
| ) | ||
| self.fused_fc_norms = ( | ||
| nn.ModuleList( | ||
| [ | ||
| FusedRMSNorm(self.fc_norm[i], self.fc_norm[i + 1]) | ||
| for i in range(0, self.num_fc_input_dim - 1, 2) | ||
| ] | ||
| ) | ||
| if self.fc_norm is not None | ||
| else None | ||
| ) | ||
| self.norm_output = getattr(config, "norm_output", False) | ||
|
|
||
| def forward( | ||
|
|
@@ -471,13 +519,13 @@ def forward( | |
| ) -> torch.Tensor: | ||
|
|
||
| if input_embeds is None: | ||
| # When TP > 1 and fused allreduce+norm is available, skip the | ||
| # NCCL allreduce in the embedding and let the midlayer fuse it | ||
| # with the input_layernorm. | ||
| # When the embedding is vocab-parallel (tp_size > 1), skip its | ||
| # NCCL allreduce and let the midlayer fuse it with input_layernorm. | ||
| # Non-sharded embeddings (tp_size == 1) need no allreduce at all. | ||
| midlayer = self.midlayer | ||
| num_tokens = input_ids.shape[0] | ||
| fuse_embed_reduce = ( | ||
| self.mapping.attn.tp_size > 1 | ||
| self.embed_tokens.tp_size > 1 | ||
| and midlayer.comm_manager.should_fuse(num_tokens) | ||
| ) | ||
| embeds = self.embed_tokens(input_ids, reduce_results=not fuse_embed_reduce) | ||
|
|
@@ -491,15 +539,23 @@ def forward( | |
| if hidden_states.size(-1) != embeds.size(-1): | ||
| if self.input_norm is not None: | ||
| hidden_states = self.input_norm(hidden_states) | ||
| if self.fc_norm is not None: | ||
| if self.fc_norm is not None and hidden_states.shape[0] > 0: | ||
| chunks = hidden_states.chunk(self.num_fc_input_dim, dim=-1) | ||
| hidden_states = torch.cat( | ||
| [ | ||
| norm(chunk) | ||
| for norm, chunk in zip(self.fc_norm, chunks, strict=True) | ||
| ], | ||
| dim=-1, | ||
| ) | ||
| normed = torch.empty_like(hidden_states) | ||
| out_chunks = normed.chunk(self.num_fc_input_dim, dim=-1) | ||
| i = 0 | ||
| for fused in self.fused_fc_norms: | ||
| fused( | ||
| input_q_a=chunks[i], | ||
| input_kv_a=chunks[i + 1], | ||
| output_q_a=out_chunks[i], | ||
| output_kv_a=out_chunks[i + 1], | ||
| ) | ||
| i += 2 | ||
| if i < self.num_fc_input_dim: | ||
| # Odd count: single norm into the last slice. | ||
| self.fc_norm[i](chunks[i], out=out_chunks[i]) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎. |
||
| hidden_states = normed | ||
| hidden_states, _ = self.fc(hidden_states) | ||
|
|
||
| residual = None | ||
|
|
@@ -673,6 +729,22 @@ def set_embed_and_head(self, embed, head): | |
| and self.config.target_hidden_size != self.config.hidden_size | ||
| ): | ||
| return | ||
| if embed.shape != self.model.embed_tokens.weight.shape: | ||
| # Target embedding layout differs (e.g. replicated instead of | ||
| # vocab-parallel); rebuild the draft embedding to match it. | ||
| with torch.device("meta"): | ||
| replicated = VocabParallelEmbedding( | ||
| self.config.vocab_size, | ||
| self.config.hidden_size, | ||
| prefix="model.embed_tokens", | ||
| ) | ||
| if embed.shape != replicated.weight.shape: | ||
| raise ValueError( | ||
| f"Cannot share target embed of shape {tuple(embed.shape)}: " | ||
| f"draft expects {tuple(self.model.embed_tokens.weight.shape)} " | ||
| f"(sharded) or {tuple(replicated.weight.shape)} (replicated)." | ||
| ) | ||
| self.model.embed_tokens = replicated | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a draft config has Useful? React with 👍 / 👎. |
||
| del self.model.embed_tokens.weight | ||
| self.model.embed_tokens.weight = embed | ||
| if head is not None and self.load_lm_head_from_target: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
fc_norm=Trueandnum_fc_input_dimis odd, this passesout_chunks[i], a column slice of the combined[N, num_fc_input_dim * H]buffer, toRMSNorm. That slice is not contiguous because its row stride is the full concatenated width; the AMD/Tritonrmsnormpath rejects non-contiguousoutbuffers, so the Eagle3 MLA draft forward fails on the final aux-state norm. Normalize the odd chunk into a contiguous temporary or teachRMSNormto write strided outputs before packing it intonormed.Useful? React with 👍 / 👎.