-
Notifications
You must be signed in to change notification settings - Fork 2.5k
[None][perf] Close Mamba hybrid warmup gap in autotuner warmup #16177
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
base: main
Are you sure you want to change the base?
Changes from all commits
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 | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1238,7 +1238,22 @@ def trtllm_gen_fmha_jit_warmup(): | |||||||||||||||||||||||
| torch.cuda.synchronize() | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def _run_autotuner_warmup(self, resource_manager: ResourceManager): | ||||||||||||||||||||||||
| """Runs a forward pass to populate the autotuner cache.""" | ||||||||||||||||||||||||
| """Runs forward passes over representative shapes to populate the autotuner cache. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Sweeps pure-ctx-max plus a small set of mixed ctx+gen shapes so that a | ||||||||||||||||||||||||
| serve workload's early iterations (context ingestion overlapping with | ||||||||||||||||||||||||
| already-running decode) don't hit an autotune cache miss on their | ||||||||||||||||||||||||
| (M,N,K) tuples and stall the stream doing in-place tuning under load. | ||||||||||||||||||||||||
| Set env TLLM_AUTOTUNE_SWEEP=0 to disable and fall back to the | ||||||||||||||||||||||||
| single-shape (curr_max_num_tokens, 0) sweep. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Mamba hybrid models (e.g. Nemotron 3 Super 120B) also add two extra | ||||||||||||||||||||||||
| passes: a multi-sequence prefill (least_requests=False) and a | ||||||||||||||||||||||||
| multi-sequence prefill with HAS_INITSTATES=True forced via env var, | ||||||||||||||||||||||||
| so the Mamba SSD Triton kernels' multi-seq + chunked-prefill | ||||||||||||||||||||||||
| constexpr variants compile during warmup instead of at iter 3. | ||||||||||||||||||||||||
| Set env TLLM_MAMBA_MULTISEQ_WARMUP=0 to disable. | ||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||
| if not self.llm_args.enable_autotuner: | ||||||||||||||||||||||||
| return | ||||||||||||||||||||||||
| AutoTuner.get().setup_distributed_state(self.mapping, self.dist) | ||||||||||||||||||||||||
|
|
@@ -1251,39 +1266,79 @@ def _run_autotuner_warmup(self, resource_manager: ResourceManager): | |||||||||||||||||||||||
| token_num_upper_bound=token_num_upper_bound, | ||||||||||||||||||||||||
| max_num_draft_tokens=self.original_max_draft_len) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # Each shape: (num_tokens, num_gen_requests, least_requests, force_mamba_initstates) | ||||||||||||||||||||||||
| autotune_shapes = [(curr_max_num_tokens, 0, True, False)] | ||||||||||||||||||||||||
| if os.environ.get("TLLM_AUTOTUNE_SWEEP", "1") == "1": | ||||||||||||||||||||||||
| if curr_max_num_tokens >= 4: | ||||||||||||||||||||||||
| autotune_shapes.append((curr_max_num_tokens, 2, True, False)) | ||||||||||||||||||||||||
| if curr_max_num_tokens >= 16: | ||||||||||||||||||||||||
| autotune_shapes.append((curr_max_num_tokens, 8, True, False)) | ||||||||||||||||||||||||
|
Comment on lines
+1272
to
+1275
Contributor
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. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Account for speculative tokens before adding mixed warmup shapes. These guards only check Suggested fix+ gen_tokens_per_request = 1 + self.max_total_draft_tokens
if curr_max_num_tokens >= 4:
- autotune_shapes.append((curr_max_num_tokens, 2, True, False))
+ if curr_max_num_tokens > 2 * gen_tokens_per_request:
+ autotune_shapes.append((curr_max_num_tokens, 2, True, False))
if curr_max_num_tokens >= 16:
- autotune_shapes.append((curr_max_num_tokens, 8, True, False))
+ if curr_max_num_tokens > 8 * gen_tokens_per_request:
+ autotune_shapes.append((curr_max_num_tokens, 8, True, False))📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| is_mamba_hybrid = isinstance(kv_cache_manager, MambaHybridCacheManager) | ||||||||||||||||||||||||
| if is_mamba_hybrid and os.environ.get( | ||||||||||||||||||||||||
| "TLLM_MAMBA_MULTISEQ_WARMUP", "1") == "1": | ||||||||||||||||||||||||
| if curr_max_num_tokens >= 4: | ||||||||||||||||||||||||
| autotune_shapes.append( | ||||||||||||||||||||||||
| (curr_max_num_tokens, 0, False, False)) | ||||||||||||||||||||||||
| autotune_shapes.append( | ||||||||||||||||||||||||
| (curr_max_num_tokens, 0, False, True)) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| logger.info( | ||||||||||||||||||||||||
| f"[Autotuner] Warmup sweep shapes (num_tokens, num_gen_requests, least_requests, force_mamba_initstates): {autotune_shapes}" | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @contextlib.contextmanager | ||||||||||||||||||||||||
| def _force_mamba_initstates_env(): | ||||||||||||||||||||||||
| prev = os.environ.get("TLLM_MAMBA_WARMUP_FORCE_INITSTATES") | ||||||||||||||||||||||||
| os.environ["TLLM_MAMBA_WARMUP_FORCE_INITSTATES"] = "1" | ||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||
| yield | ||||||||||||||||||||||||
| finally: | ||||||||||||||||||||||||
| if prev is None: | ||||||||||||||||||||||||
| os.environ.pop( | ||||||||||||||||||||||||
| "TLLM_MAMBA_WARMUP_FORCE_INITSTATES", None) | ||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||
| os.environ["TLLM_MAMBA_WARMUP_FORCE_INITSTATES"] = prev | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| cache_path = os.environ.get("TLLM_AUTOTUNER_CACHE_PATH", None) | ||||||||||||||||||||||||
| with self.no_cuda_graph(), autotune(cache_path=cache_path): | ||||||||||||||||||||||||
| warmup_request = self._create_warmup_request( | ||||||||||||||||||||||||
| resource_manager, curr_max_num_tokens, 0) | ||||||||||||||||||||||||
| with self._release_batch_context(warmup_request, | ||||||||||||||||||||||||
| resource_manager) as batch: | ||||||||||||||||||||||||
| if batch is None and self.mapping.tp_size <= 1: | ||||||||||||||||||||||||
| pass # Single rank, safe to skip | ||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||
| self._assert_all_tp_ranks_have_warmup_batch( | ||||||||||||||||||||||||
| batch, curr_max_num_tokens) | ||||||||||||||||||||||||
| if batch is not None: | ||||||||||||||||||||||||
| # Reset the flag is_first_draft for the draft model. | ||||||||||||||||||||||||
| # This is necessary for overlap scheduler. | ||||||||||||||||||||||||
| spec_resource_manager = resource_manager.get_resource_manager( | ||||||||||||||||||||||||
| ResourceManagerType.SPEC_RESOURCE_MANAGER) | ||||||||||||||||||||||||
| if self.is_draft_model and isinstance( | ||||||||||||||||||||||||
| spec_resource_manager, Eagle3ResourceManager): | ||||||||||||||||||||||||
| spec_resource_manager.is_first_draft = True | ||||||||||||||||||||||||
| for (num_tokens_i, num_gen_requests_i, least_req_i, | ||||||||||||||||||||||||
| force_init_i) in autotune_shapes: | ||||||||||||||||||||||||
| init_ctx = (_force_mamba_initstates_env() | ||||||||||||||||||||||||
| if force_init_i else contextlib.nullcontext()) | ||||||||||||||||||||||||
| with init_ctx: | ||||||||||||||||||||||||
| warmup_request = self._create_warmup_request( | ||||||||||||||||||||||||
| resource_manager, num_tokens_i, num_gen_requests_i, | ||||||||||||||||||||||||
| least_requests=least_req_i) | ||||||||||||||||||||||||
| with self._release_batch_context( | ||||||||||||||||||||||||
| warmup_request, resource_manager) as batch: | ||||||||||||||||||||||||
| if batch is None and self.mapping.tp_size <= 1: | ||||||||||||||||||||||||
| continue # Single rank, safe to skip this shape | ||||||||||||||||||||||||
| self._assert_all_tp_ranks_have_warmup_batch( | ||||||||||||||||||||||||
| batch, num_tokens_i) | ||||||||||||||||||||||||
| if batch is None: | ||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||
| # Reset the flag is_first_draft for the draft model. | ||||||||||||||||||||||||
| # This is necessary for overlap scheduler. | ||||||||||||||||||||||||
| spec_resource_manager = resource_manager.get_resource_manager( | ||||||||||||||||||||||||
| ResourceManagerType.SPEC_RESOURCE_MANAGER) | ||||||||||||||||||||||||
| if self.is_draft_model and isinstance( | ||||||||||||||||||||||||
| spec_resource_manager, Eagle3ResourceManager): | ||||||||||||||||||||||||
| spec_resource_manager.is_first_draft = True | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| self.forward(batch, | ||||||||||||||||||||||||
| new_tensors_device=None, | ||||||||||||||||||||||||
| resource_manager=resource_manager) | ||||||||||||||||||||||||
| self.forward(batch, | ||||||||||||||||||||||||
| new_tensors_device=None, | ||||||||||||||||||||||||
| resource_manager=resource_manager) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # pp_recv in AutoTuner choose_one will never be called if there is no tuning op during the forward pass. | ||||||||||||||||||||||||
| # So we need to make an extra call to consume the previous rank's pp_send to guarantee that the previous rank's pp_send is released. | ||||||||||||||||||||||||
| AutoTuner.get().cache_pp_recv() | ||||||||||||||||||||||||
| # Send the cache after the tuning process to the next PP rank | ||||||||||||||||||||||||
| AutoTuner.get().cache_pp_send() | ||||||||||||||||||||||||
| # Clean the pp flag to avoid deadlock with synchronous send/recv | ||||||||||||||||||||||||
| AutoTuner.get().clean_pp_flag() | ||||||||||||||||||||||||
| # pp_recv in AutoTuner choose_one will never be called if there is no tuning op during the forward pass. | ||||||||||||||||||||||||
| # So we need to make an extra call to consume the previous rank's pp_send to guarantee that the previous rank's pp_send is released. | ||||||||||||||||||||||||
| AutoTuner.get().cache_pp_recv() | ||||||||||||||||||||||||
| # Send the cache after the tuning process to the next PP rank | ||||||||||||||||||||||||
| AutoTuner.get().cache_pp_send() | ||||||||||||||||||||||||
| # Clean the pp flag to avoid deadlock with synchronous send/recv | ||||||||||||||||||||||||
| AutoTuner.get().clean_pp_flag() | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| torch.cuda.synchronize() | ||||||||||||||||||||||||
| torch.cuda.synchronize() | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| logger.info( | ||||||||||||||||||||||||
| f"[Autotuner] Cache size after warmup is {len(AutoTuner.get().profiling_cache)}" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
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.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Keep the init-state override scoped to warmup.
Mamba2Metadata.prepare()now honorsTLLM_MAMBA_WARMUP_FORCE_INITSTATESon every call. If that variable is set at process scope, real prefill requests with zero cached tokens are treated as having initial states. Prefer passing an explicit warmup-only flag through metadata instead of reading global env here.🤖 Prompt for AI Agents