From c07cfda8e595d14ed10dca80841a63e87dc38633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Quang=20H=C3=B2a?= Date: Mon, 24 Aug 2026 10:52:30 +0700 Subject: [PATCH] fix: fold DP_SIZE into single-node num_gpus for per-GPU throughput process_result.py's single-node branch computed num_gpus as tp_size * pp * pcp_size, with no data-parallel term. Any single-node config running --data-parallel-size N without exporting that as TP/PP/ PCP_SIZE (e.g. tensor-parallel-size 1 + data-parallel-size 2, N full replicas on N GPUs) divided total_token_throughput by 1 instead of N, inflating tput_per_gpu / output_tput_per_gpu / input_tput_per_gpu by a factor of N. Read an optional DP_SIZE env var (defaults to 1, so existing configs are unaffected) and fold it into num_gpus. Also record dp_size in the output JSON alongside the existing pp/dcp_size/pcp_size fields. Fixes #2714 --- utils/process_result.py | 8 ++++-- utils/test_process_result.py | 53 ++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/utils/process_result.py b/utils/process_result.py index fcc41ead3d..c314aed8e6 100644 --- a/utils/process_result.py +++ b/utils/process_result.py @@ -249,9 +249,10 @@ def record_power_internal_error( pp = int(os.environ.get('PP_SIZE', '1')) dcp_size = int(os.environ.get('DCP_SIZE', '1')) pcp_size = int(os.environ.get('PCP_SIZE', '1')) - if pp <= 0 or dcp_size <= 0 or pcp_size <= 0: - raise ValueError("PP_SIZE, DCP_SIZE, and PCP_SIZE must be positive integers.") - num_gpus = tp_size * pp * pcp_size + dp_size = int(os.environ.get('DP_SIZE', '1')) + if pp <= 0 or dcp_size <= 0 or pcp_size <= 0 or dp_size <= 0: + raise ValueError("PP_SIZE, DCP_SIZE, PCP_SIZE, and DP_SIZE must be positive integers.") + num_gpus = tp_size * pp * pcp_size * dp_size single_node_data = { 'is_multinode': False, @@ -259,6 +260,7 @@ def record_power_internal_error( 'pp': pp, 'dcp_size': dcp_size, 'pcp_size': pcp_size, + 'dp_size': dp_size, 'ep': ep_size, 'dp_attention': dp_attention, 'tput_per_gpu': float(bmk_result['total_token_throughput']) / num_gpus, diff --git a/utils/test_process_result.py b/utils/test_process_result.py index 360a5ae0b3..b5196d2b96 100644 --- a/utils/test_process_result.py +++ b/utils/test_process_result.py @@ -459,6 +459,59 @@ def test_throughput_per_gpu_single_node(self, tmp_path, single_node_env_vars): assert output_data["output_tput_per_gpu"] == pytest.approx(6000.0 / 16) assert output_data["input_tput_per_gpu"] == pytest.approx(2000.0 / 16) + def test_throughput_per_gpu_single_node_default_dp_size(self, tmp_path, single_node_env_vars): + """DP_SIZE defaults to 1 when unset, so existing single-node configs are unaffected.""" + benchmark_result = { + "model_id": "test-model", + "max_concurrency": 8, + "total_token_throughput": 8000.0, + "output_throughput": 6000.0, + } + + result = run_script(tmp_path, single_node_env_vars, benchmark_result) + assert result.returncode == 0, f"Script failed: {result.stderr}" + + output_data = json.loads(result.stdout) + assert output_data["dp_size"] == 1 + assert output_data["tput_per_gpu"] == pytest.approx(8000.0 / 8) + + def test_throughput_per_gpu_single_node_with_dp_size(self, tmp_path, single_node_env_vars): + """DP_SIZE multiplies the GPU denominator, e.g. --tensor-parallel-size 1 + --data-parallel-size 2 running two full-model replicas on 2 GPUs.""" + benchmark_result = { + "model_id": "test-model", + "max_concurrency": 8, + "total_token_throughput": 8000.0, + "output_throughput": 6000.0, + } + + env = single_node_env_vars.copy() + env.update({"TP": "1", "DP_SIZE": "2"}) + + result = run_script(tmp_path, env, benchmark_result) + assert result.returncode == 0, f"Script failed: {result.stderr}" + + output_data = json.loads(result.stdout) + assert output_data["dp_size"] == 2 + assert output_data["tput_per_gpu"] == pytest.approx(8000.0 / 2) + assert output_data["output_tput_per_gpu"] == pytest.approx(6000.0 / 2) + assert output_data["input_tput_per_gpu"] == pytest.approx(2000.0 / 2) + + def test_invalid_dp_size_raises_error(self, tmp_path, single_node_env_vars): + benchmark_result = { + "model_id": "test-model", + "max_concurrency": 8, + "total_token_throughput": 8000.0, + "output_throughput": 6000.0, + } + + env = single_node_env_vars.copy() + env.update({"DP_SIZE": "0"}) + + result = run_script(tmp_path, env, benchmark_result) + assert result.returncode != 0 + assert "DP_SIZE" in result.stderr + def test_throughput_per_gpu_multinode(self, tmp_path, multinode_env_vars): """Test throughput per GPU calculation for multinode.""" benchmark_result = {