-
Notifications
You must be signed in to change notification settings - Fork 2k
[TRTLLM-6759] Disagg-serving + PP tests on top of #6369 #6398
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
00c77b3
11cff8a
1cd5d8f
dc16770
f94e76f
d2900d7
5997e07
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 | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -71,7 +71,10 @@ def clear_folder(folder_path): | |||||||||||||||||
| if os.path.isdir(item_path) and not os.path.islink(item_path): | ||||||||||||||||||
| rmtree(item_path) | ||||||||||||||||||
| else: | ||||||||||||||||||
| os.remove(item_path) | ||||||||||||||||||
| try: | ||||||||||||||||||
| os.remove(item_path) | ||||||||||||||||||
| except: | ||||||||||||||||||
| print(f"failed to remove {item_path}") | ||||||||||||||||||
|
Comment on lines
+74
to
+77
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. Improve error handling specificity and reporting. The bare Apply this diff to improve error handling: - try:
- os.remove(item_path)
- except:
- print(f"failed to remove {item_path}")
+ try:
+ os.remove(item_path)
+ except (OSError, IOError) as e:
+ print(f"Failed to remove {item_path}: {e}", file=sys.stderr)This change:
📝 Committable suggestion
Suggested change
🧰 Tools🪛 Ruff (0.12.2)76-76: Do not use bare (E722) 🤖 Prompt for AI Agents
Collaborator
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. @reasonsolo can we apply this suggestion?
Collaborator
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.
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.
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.
|
||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| def sysconfig_scheme(override_vars=None): | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -122,6 +122,7 @@ class BatchState: | |||||||||||||||||
| @dataclasses.dataclass | ||||||||||||||||||
| class BatchStatePP(BatchState): | ||||||||||||||||||
| microbatch_id: int = -1 | ||||||||||||||||||
| scheduled_ctx_reqs: list[LlmRequest] = None | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| class PyExecutor: | ||||||||||||||||||
|
|
@@ -643,6 +644,7 @@ def _need_return_log_probs(self, scheduled_requests: ScheduledRequests): | |||||||||||||||||
| return False | ||||||||||||||||||
|
|
||||||||||||||||||
| def _executor_loop_pp(self): | ||||||||||||||||||
| logger.info(f"Starting executor loop for pp_rank {self.dist.pp_rank}") | ||||||||||||||||||
| torch.cuda.set_device(self.device_id) | ||||||||||||||||||
| microbatch_id = 0 | ||||||||||||||||||
| with self._profiler() as profile_step: | ||||||||||||||||||
|
|
@@ -656,6 +658,9 @@ def _executor_loop_pp(self): | |||||||||||||||||
| if self.should_stop_processing: | ||||||||||||||||||
| break | ||||||||||||||||||
|
|
||||||||||||||||||
| if self.kv_cache_transceiver: | ||||||||||||||||||
| self._check_disagg_gen_transfer_status() | ||||||||||||||||||
|
|
||||||||||||||||||
| if self.enable_iter_perf_stats: | ||||||||||||||||||
| iter_stats = self._get_init_iter_stats( | ||||||||||||||||||
| len(new_requests), | ||||||||||||||||||
|
|
@@ -664,9 +669,27 @@ def _executor_loop_pp(self): | |||||||||||||||||
|
|
||||||||||||||||||
| self._pad_attention_dp_dummy_request() | ||||||||||||||||||
|
|
||||||||||||||||||
| scheduled_batch, _, _ = self._schedule() | ||||||||||||||||||
| scheduled_batch, fitting_disagg_gen_init_requests, num_fitting_reqs = self._schedule( | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| if self.kv_cache_transceiver: | ||||||||||||||||||
| # For requests that are fitting disagg gen init, also prepare resources for KV cache manager | ||||||||||||||||||
| self._prepare_disagg_gen_init( | ||||||||||||||||||
| fitting_disagg_gen_init_requests) | ||||||||||||||||||
|
|
||||||||||||||||||
| if num_fitting_reqs == 0 and not fitting_disagg_gen_init_requests: | ||||||||||||||||||
| logger.warning( | ||||||||||||||||||
| "num_fitting_reqs=0 and fitting_disagg_gen_init_requests is empty, may not have enough kvCache" | ||||||||||||||||||
| ) | ||||||||||||||||||
| self.kv_cache_transceiver.check_context_transfer_status( | ||||||||||||||||||
| 1) | ||||||||||||||||||
| else: | ||||||||||||||||||
| assert scheduled_batch.batch_size > 0, ( | ||||||||||||||||||
| "fail to schedule any pending request, " | ||||||||||||||||||
| "probably run out of resource.") | ||||||||||||||||||
|
|
||||||||||||||||||
pcastonguay marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
| self.num_scheduled_requests = scheduled_batch.batch_size | ||||||||||||||||||
|
|
||||||||||||||||||
| logger.debug( | ||||||||||||||||||
| f'has {len(self.active_requests)} active_request, ' | ||||||||||||||||||
| f'scheduled {len(scheduled_batch.context_requests)} context requests and ' | ||||||||||||||||||
|
|
@@ -679,7 +702,7 @@ def _executor_loop_pp(self): | |||||||||||||||||
| can_queue = 0 not in tp_batch_sizes | ||||||||||||||||||
| else: | ||||||||||||||||||
| can_queue = scheduled_batch.batch_size > 0 | ||||||||||||||||||
| if not can_queue: | ||||||||||||||||||
| if not can_queue and not self.kv_cache_transceiver: | ||||||||||||||||||
| assert len(self.inflight_req_ids) > 0, ( | ||||||||||||||||||
| "fail to schedule any pending request, probably run out of resource" | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
@@ -688,8 +711,28 @@ def _executor_loop_pp(self): | |||||||||||||||||
| self.micro_batches[microbatch_id] = None | ||||||||||||||||||
| else: | ||||||||||||||||||
| self._add_inflight_ids(scheduled_batch) | ||||||||||||||||||
|
|
||||||||||||||||||
| if self.kv_cache_transceiver: | ||||||||||||||||||
| # For generation requests which have completed KV cache transfer | ||||||||||||||||||
| self._prepare_disagg_gen_transmission_complete( | ||||||||||||||||||
| scheduled_batch) | ||||||||||||||||||
|
|
||||||||||||||||||
| self.resource_manager.prepare_resources(scheduled_batch) | ||||||||||||||||||
|
|
||||||||||||||||||
| # The generation requests that are do not have batch_idx, | ||||||||||||||||||
| # needs to be in front of the batch due to the assumptions | ||||||||||||||||||
| # made in model_engine.py::_forward_step. This is only important | ||||||||||||||||||
| # for disaggregated serving. For non-disaggregated serving, | ||||||||||||||||||
| # the generation requests always have batch_idx. | ||||||||||||||||||
| scheduled_batch.generation_requests = sorted( # stable sort | ||||||||||||||||||
| scheduled_batch.generation_requests, | ||||||||||||||||||
| key=lambda req: int(req.py_batch_idx is not None), | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
pcastonguay marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
| if self.kv_cache_transceiver: | ||||||||||||||||||
| # Return the first token to the client | ||||||||||||||||||
| self._handle_first_token_response(scheduled_batch) | ||||||||||||||||||
|
|
||||||||||||||||||
| # Stage 1: Async forward (all ranks) and decoding pass (last rank only) | ||||||||||||||||||
| if not self.dist.is_last_pp_rank: | ||||||||||||||||||
| sample_state = self._forward_step_inter_pp( | ||||||||||||||||||
|
|
@@ -720,6 +763,7 @@ def _executor_loop_pp(self): | |||||||||||||||||
| iter_start_time=iter_start_time, | ||||||||||||||||||
| iter_stats=iter_stats, | ||||||||||||||||||
| microbatch_id=microbatch_id, | ||||||||||||||||||
| scheduled_ctx_reqs=scheduled_batch.context_requests, | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| self.micro_batches[microbatch_id] = batch_state | ||||||||||||||||||
|
|
@@ -784,6 +828,12 @@ def _executor_loop_pp(self): | |||||||||||||||||
| if previous_batch is not None: | ||||||||||||||||||
| with torch.cuda.nvtx.range("_handle_previous_batch_pp"): | ||||||||||||||||||
| self._update_requests(previous_batch.sample_state) | ||||||||||||||||||
|
|
||||||||||||||||||
| if self.kv_cache_transceiver and previous_batch.scheduled_ctx_reqs: | ||||||||||||||||||
| self._send_disagg_ctx_cache( | ||||||||||||||||||
| previous_batch.scheduled_ctx_reqs | ||||||||||||||||||
| ) if self.kv_cache_transceiver else [] | ||||||||||||||||||
|
|
||||||||||||||||||
|
Comment on lines
+832
to
+836
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. 🛠️ Refactor suggestion Remove redundant conditional check. The ternary operator on line 835 is redundant since the code is already inside an - if self.kv_cache_transceiver and previous_batch.scheduled_ctx_reqs:
- self._send_disagg_ctx_cache(
- previous_batch.scheduled_ctx_reqs
- ) if self.kv_cache_transceiver else []
+ if self.kv_cache_transceiver and previous_batch.scheduled_ctx_reqs:
+ self._send_disagg_ctx_cache(
+ previous_batch.scheduled_ctx_reqs
+ )📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Collaborator
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. @reasonsolo @raayandhar can you apply this suggestion.
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.
|
||||||||||||||||||
| self._handle_canceled_requests() | ||||||||||||||||||
| finished_requests = self._handle_responses() | ||||||||||||||||||
| previous_scheduled_batch = previous_batch.sample_state.scheduled_requests | ||||||||||||||||||
|
|
@@ -792,6 +842,9 @@ def _executor_loop_pp(self): | |||||||||||||||||
| self._remove_inflight_ids(previous_scheduled_batch) | ||||||||||||||||||
| self.micro_batches[prev_microbatch_id] = None | ||||||||||||||||||
|
|
||||||||||||||||||
| if self.kv_cache_transceiver and self.ctx_in_transmission_requests: | ||||||||||||||||||
| self._terminate_ctx_finished_requests() | ||||||||||||||||||
|
|
||||||||||||||||||
| # march forward in microbatch slots | ||||||||||||||||||
| microbatch_id = (microbatch_id + 1) % self.num_micro_batches | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -20,9 +20,11 @@ | |||||||||||||
| from tensorrt_llm.llmapi import CompletionOutput, RequestOutput, SamplingParams | ||||||||||||||
| from tensorrt_llm.llmapi.llm_args import LlmArgs | ||||||||||||||
|
|
||||||||||||||
| from ..conftest import llm_models_root, parametrize_with_ids, skip_pre_hopper | ||||||||||||||
| from ..conftest import (get_device_count, llm_models_root, parametrize_with_ids, | ||||||||||||||
| skip_pre_hopper) | ||||||||||||||
| from ..trt_test_alternative import popen | ||||||||||||||
| from .accuracy_core import GSM8K, MMLU, LlmapiAccuracyTestHarness | ||||||||||||||
| from .accuracy_core import (GSM8K, MMLU, LlmapiAccuracyTestHarness, | ||||||||||||||
| get_accuracy_task) | ||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| class Result(GenerationResultBase): | ||||||||||||||
|
|
@@ -71,6 +73,12 @@ def launch_disaggregated_llm(disaggregated_server_config: Dict[str, Any], | |||||||||||||
| temp_dir = tempfile.TemporaryDirectory() | ||||||||||||||
| disaggregated_serving_config_path = os.path.join( | ||||||||||||||
| temp_dir.name, "disaggregated_serving_config.yaml") | ||||||||||||||
|
|
||||||||||||||
| if tensor_parallel_size > 1: | ||||||||||||||
| print( | ||||||||||||||
| f"Using unified tp parameter for testing is not recommended. Please use server configs instead." | ||||||||||||||
| ) | ||||||||||||||
|
|
||||||||||||||
| with open(disaggregated_serving_config_path, "w") as f: | ||||||||||||||
| yaml.dump(disaggregated_server_config, f) | ||||||||||||||
| ctx_server_config_path = os.path.join(temp_dir.name, | ||||||||||||||
|
|
@@ -88,27 +96,38 @@ def launch_disaggregated_llm(disaggregated_server_config: Dict[str, Any], | |||||||||||||
| trtllm_serve_path = "trtllm-serve" | ||||||||||||||
| # Common arguments for both servers | ||||||||||||||
| common_args = [ | ||||||||||||||
| trtllm_serve_path, model_name, "--host", "localhost", "--backend", | ||||||||||||||
| "pytorch" | ||||||||||||||
| trtllm_serve_path, | ||||||||||||||
| model_name, | ||||||||||||||
| "--host", | ||||||||||||||
| "localhost", | ||||||||||||||
| "--backend", | ||||||||||||||
| "pytorch", | ||||||||||||||
| ] | ||||||||||||||
| gen_tp, gen_pp = gen_server_config.get("tensor_parallel_size", | ||||||||||||||
| 1), gen_server_config.get( | ||||||||||||||
| "pipeline_parallel_size", 1) | ||||||||||||||
| ctx_tp, ctx_pp = ctx_server_config.get("tensor_parallel_size", | ||||||||||||||
| 1), ctx_server_config.get( | ||||||||||||||
| "pipeline_parallel_size", 1) | ||||||||||||||
|
|
||||||||||||||
| if tensor_parallel_size > 1: | ||||||||||||||
| common_args.append(f"--tp_size={tensor_parallel_size}") | ||||||||||||||
| ctx_total_gpus = ctx_tp * ctx_pp | ||||||||||||||
| gen_total_gpus = gen_tp * gen_pp | ||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
|
|
||||||||||||||
| env_ctx = os.environ.copy() | ||||||||||||||
| env_ctx["TRTLLM_USE_UCX_KVCACHE"] = "1" | ||||||||||||||
| env_ctx["CUDA_VISIBLE_DEVICES"] = ",".join( | ||||||||||||||
| map(str, range(tensor_parallel_size))) | ||||||||||||||
| env_ctx["CUDA_VISIBLE_DEVICES"] = ",".join(map(str, range(ctx_total_gpus))) | ||||||||||||||
|
|
||||||||||||||
| env_gen = os.environ.copy() | ||||||||||||||
| env_gen["TRTLLM_USE_UCX_KVCACHE"] = "1" | ||||||||||||||
| env_gen["CUDA_VISIBLE_DEVICES"] = ",".join( | ||||||||||||||
| map(str, range(tensor_parallel_size, 2 * tensor_parallel_size))) | ||||||||||||||
| map(str, range(ctx_total_gpus, ctx_total_gpus + gen_total_gpus))) | ||||||||||||||
| ctx_server_args = common_args + [ | ||||||||||||||
| "--port", "8001", "--extra_llm_api_options", ctx_server_config_path | ||||||||||||||
| "--port", "8001", "--extra_llm_api_options", ctx_server_config_path, | ||||||||||||||
| f"--tp_size={ctx_tp}", f"--pp_size={ctx_pp}" | ||||||||||||||
| ] | ||||||||||||||
| gen_server_args = common_args + [ | ||||||||||||||
| "--port", "8002", "--extra_llm_api_options", gen_server_config_path | ||||||||||||||
| "--port", "8002", "--extra_llm_api_options", gen_server_config_path, | ||||||||||||||
| f"--tp_size={gen_tp}", f"--pp_size={gen_pp}" | ||||||||||||||
| ] | ||||||||||||||
| if "max_num_tokens" in ctx_server_config: | ||||||||||||||
| ctx_server_args.append( | ||||||||||||||
|
|
@@ -315,6 +334,69 @@ def test_eagle3(self, overlap_scheduler): | |||||||||||||
| task = GSM8K(self.MODEL_NAME) | ||||||||||||||
| task.evaluate(llm) | ||||||||||||||
|
|
||||||||||||||
| def run_parallel_test(self, ctx_pp: int, ctx_tp: int, gen_pp: int, | ||||||||||||||
| gen_tp: int, test_set: LlmapiAccuracyTestHarness): | ||||||||||||||
| if ctx_tp * ctx_pp + gen_tp * gen_pp > get_device_count(): | ||||||||||||||
| pytest.skip( | ||||||||||||||
| f"Not enough devices for ctx_pp={ctx_pp}+ctx_tp={ctx_tp} and gen_pp={gen_pp}+gen_tp={gen_tp} test" | ||||||||||||||
| ) | ||||||||||||||
|
|
||||||||||||||
| kv_cache_config = { | ||||||||||||||
| "free_gpu_memory_fraction": 0.5, | ||||||||||||||
| "enable_block_reuse": False | ||||||||||||||
| } | ||||||||||||||
| ctx_server_config = { | ||||||||||||||
| "pipeline_parallel_size": ctx_pp, | ||||||||||||||
| "tensor_parallel_size": ctx_tp, | ||||||||||||||
| "disable_overlap_scheduler": True, | ||||||||||||||
| "kv_cache_config": kv_cache_config, | ||||||||||||||
| "cache_transceiver_config": { | ||||||||||||||
| "backend": "default" | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| gen_server_config = { | ||||||||||||||
| "tensor_parallel_size": gen_tp, | ||||||||||||||
| "pipeline_parallel_size": gen_pp, | ||||||||||||||
| "disable_overlap_scheduler": True, | ||||||||||||||
| "kv_cache_config": kv_cache_config, | ||||||||||||||
| "cache_transceiver_config": { | ||||||||||||||
| "backend": "default" | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| disaggregated_server_config = { | ||||||||||||||
| "hostname": "localhost", | ||||||||||||||
| "port": 8000, | ||||||||||||||
| "backend": "pytorch", | ||||||||||||||
| "context_servers": { | ||||||||||||||
| "num_instances": 1, | ||||||||||||||
| "urls": ["localhost:8001"] | ||||||||||||||
| }, | ||||||||||||||
| "generation_servers": { | ||||||||||||||
| "num_instances": 1, | ||||||||||||||
| "urls": ["localhost:8002"] | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
| with launch_disaggregated_llm(disaggregated_server_config, | ||||||||||||||
| ctx_server_config, gen_server_config, | ||||||||||||||
| self.MODEL_PATH) as llm: | ||||||||||||||
| task = test_set(self.MODEL_NAME) | ||||||||||||||
| task.evaluate(llm) | ||||||||||||||
|
|
||||||||||||||
| @pytest.mark.parametrize("tp,pp", [(1, 2), (2, 1), (2, 2)], | ||||||||||||||
| ids=["tp1pp2", "tp2pp1", "tp2pp2"]) | ||||||||||||||
| @pytest.mark.parametrize("testset", ["GSM8K", "MMLU"]) | ||||||||||||||
| def test_tp_pp_symmetric(self, tp, pp, testset): | ||||||||||||||
| return self.run_parallel_test(pp, tp, pp, tp, | ||||||||||||||
| get_accuracy_task(testset)) | ||||||||||||||
|
|
||||||||||||||
| # We focus on ctx+pp and gen+tp usecases for RTX6000D | ||||||||||||||
| @parametrize_with_ids("ctx_pp", [2, 4]) | ||||||||||||||
| @parametrize_with_ids("gen_tp", [1, 2]) | ||||||||||||||
| @pytest.mark.parametrize("testset", ["GSM8K", "MMLU"]) | ||||||||||||||
| def test_ctx_pp_gen_tp_asymmetric(self, ctx_pp, gen_tp, testset): | ||||||||||||||
| return self.run_parallel_test(ctx_pp, 1, gen_tp, | ||||||||||||||
| get_accuracy_task(testset)) | ||||||||||||||
|
Comment on lines
+396
to
+398
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. Fix incorrect method signature in test_ctx_pp_gen_tp_asymmetric. The method is missing the def test_ctx_pp_gen_tp_asymmetric(self, ctx_pp, gen_tp, testset):
- return self.run_parallel_test(ctx_pp, 1, gen_tp,
+ return self.run_parallel_test(ctx_pp, 1, 1, gen_tp,
get_accuracy_task(testset))📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
|
|
||||||||||||||
|
|
||||||||||||||
| @pytest.mark.skip_less_device_memory(140000) | ||||||||||||||
| @pytest.mark.timeout(3600) | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| model: TinyLlama/TinyLlama-1.1B-Chat-v1.0 | ||
| hostname: localhost | ||
| port: 8000 | ||
| backend: "pytorch" | ||
| cuda_graph_config: null | ||
| free_gpu_memory_fraction: 0.2 | ||
| context_servers: | ||
| num_instances: 1 | ||
| max_batch_size: 1 | ||
| max_num_tokens: 3000 | ||
| max_seq_len: 4096 | ||
| tensor_parallel_size: 1 | ||
| pipeline_parallel_size: 2 | ||
| kv_cache_config: | ||
| free_gpu_memory_fraction: 0.2 | ||
| enable_partial_reuse: False | ||
| disable_overlap_scheduler: True | ||
| cache_transceiver_config: | ||
| backend: default | ||
| urls: | ||
| - "localhost:8001" | ||
| generation_servers: | ||
| num_instances: 1 | ||
| tensor_parallel_size: 1 | ||
| pipeline_parallel_size: 2 | ||
| max_batch_size: 256 | ||
| max_num_tokens: 4096 | ||
| max_seq_len: 4096 | ||
| kv_cache_config: | ||
| free_gpu_memory_fraction: 0.2 | ||
| enable_partial_reuse: False | ||
| disable_overlap_scheduler: True | ||
| cache_transceiver_config: | ||
| backend: default | ||
| urls: | ||
| - "localhost:8002" |
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.
Incorrect printf specifier for
size_t— will trigger warnings or UBsize()returnssize_t, yet the format string uses%d, which expectsint. On LP64/LLP64 targets this causes a type/width mismatch.📝 Committable suggestion
🤖 Prompt for AI Agents