@@ -65,12 +65,8 @@ class BenchmarkMetrics:
6565 input_throughput : float
6666 output_throughput : float
6767 total_token_throughput : float
68- # 解码速度(过滤 TPOT<1ms 的请求后)
69- s_decode_filtered_mean : float # tok/s
70- s_decode_filtered_median : float # tok/s
71- n_decode_total : int # 参与统计的总请求数
72- n_decode_filtered : int # 被过滤的请求数(TPOT<1ms)
73- n_decode_reliable : int # 可信请求数(TPOT>=1ms)
68+ # 解码速度:首 token 之后每秒真实到达的 token 数,全量统计不过滤
69+ n_decode_total : int # 参与统计的请求数
7470 mean_s_decode : float
7571 median_s_decode : float
7672 std_s_decode : float
@@ -259,10 +255,19 @@ def calculate_metrics(
259255 else :
260256 # sglang等无arrival_time场景fallback
261257 if outputs [i ].output_tokens > 1 :
262- decode_time = outputs [i ].latency - outputs [i ].ttft
258+ # ITL 条目数 = 首个可见 chunk 之后真实到达的 token 数(按服务端 usage 增量摊分),
259+ # sum(itl) = 对应的完整解码区间。用 len/sum 而不是 (output_tokens-1)/sum:
260+ # 首个 chunk 在 MTP 下可能一次带 1~3 个 token,减 1 会有系统偏差。
261+ # 也不用 latency-ttft:解析器攒批时被吞掉的 token 会被计进 TTFT,解码速度虚高。
262+ if outputs [i ].itl :
263+ decode_time = sum (outputs [i ].itl )
264+ decode_tokens = len (outputs [i ].itl )
265+ else :
266+ decode_time = outputs [i ].latency - outputs [i ].ttft
267+ decode_tokens = outputs [i ].output_tokens - 1
263268
264269 if decode_time > 0 :
265- s_decodes .append (( outputs [ i ]. output_tokens - 1 ) / decode_time )
270+ s_decodes .append (decode_tokens / decode_time )
266271 else :
267272 s_decodes .append (0 )
268273 else :
@@ -309,25 +314,10 @@ def calculate_metrics(
309314 total_input += prefill_only_input
310315 print (f"零输出(prefill-only)请求: { prefill_only_reqs } 条, " f"补入 ITPS 的输入 token: { prefill_only_input } " )
311316
312- # === 解码速度过滤:TPOT < 1ms 的请求视为不可信(引擎批量flush伪象) ===
313- MIN_TPOT_S = 0.001 # 1ms
314- reliable_s_decodes = []
315- n_decode_total = 0
316- n_decode_filtered = 0
317- for o in outputs :
318- if not o .success or o .output_tokens <= 1 :
319- continue
320- decode_time = sum (o .itl ) if o .itl else 0
321- if decode_time <= 0 :
322- continue
323- n_decode_total += 1
324- tokens = o .output_tokens - 1
325- tpot = decode_time / tokens
326- if tpot >= MIN_TPOT_S :
327- reliable_s_decodes .append (tokens / decode_time )
328- else :
329- n_decode_filtered += 1
330- n_decode_reliable = len (reliable_s_decodes )
317+ # 解码速度只有一份口径:上面循环里按 len(itl)/sum(itl) 逐请求算好的 s_decodes。
318+ # ITL 由服务端 usage 的 token 增量摊分得到(含被解析器攒批吞掉的 token 与收尾 token),
319+ # 所以 sum(itl) 就是真实解码区间,不需要"TPOT<1ms 视为不可信"这类兜底过滤。
320+ n_decode_total = len ([s for s in s_decodes if s > 0 ])
331321
332322 metrics = BenchmarkMetrics (
333323 completed = completed ,
@@ -338,11 +328,7 @@ def calculate_metrics(
338328 input_throughput = total_input / dur_s ,
339329 output_throughput = sum (actual_output_lens ) / dur_s ,
340330 total_token_throughput = (total_input + sum (actual_output_lens )) / dur_s ,
341- s_decode_filtered_mean = float (np .mean (reliable_s_decodes )) if reliable_s_decodes else 0.0 ,
342- s_decode_filtered_median = float (np .median (reliable_s_decodes )) if reliable_s_decodes else 0.0 ,
343331 n_decode_total = n_decode_total ,
344- n_decode_filtered = n_decode_filtered ,
345- n_decode_reliable = n_decode_reliable ,
346332 mean_s_decode = np .mean (s_decodes or 0 ) * 1 , # ttfts is empty if streaming is not supported by backend
347333 std_s_decode = np .std (s_decodes or 0 ) * 1 ,
348334 median_s_decode = np .median (s_decodes or 0 ) * 1 ,
@@ -808,11 +794,7 @@ async def limited_request_func_per_ip(req_input, semaphore, pbar):
808794 "reasoning_contents" : [output .reasoning_content for output in outputs ],
809795 "errors" : [output .error for output in outputs ],
810796 "metrics" : [output .metrics for output in outputs ],
811- "s_decode_filtered_mean" : metrics .s_decode_filtered_mean ,
812- "s_decode_filtered_median" : metrics .s_decode_filtered_median ,
813797 "n_decode_total" : metrics .n_decode_total ,
814- "n_decode_filtered" : metrics .n_decode_filtered ,
815- "n_decode_reliable" : metrics .n_decode_reliable ,
816798 }
817799
818800 def process_one_metric (
@@ -961,22 +943,8 @@ def process_one_length(
961943 print ("{:<40} {:<10.2f}" .format (f"P{ p_word } { metric_name } :" , value ))
962944 result [f"p{ p_word } _{ metric_attribute_name } " ] = value
963945
964- print ("{s:{c}^{n}}" .format (s = "解码速度 (过滤TPOT<1ms)" , n = 50 , c = "-" ))
965- _n_total = max (metrics .n_decode_total , 1 )
966- print ("{:<40} {:<10d}" .format ("Total requests:" , metrics .n_decode_total ))
967- print (
968- "{:<40} {:<10d} ({:.2f}%)" .format (
969- "Filtered (TPOT<1ms):" , metrics .n_decode_filtered , 100 * metrics .n_decode_filtered / _n_total
970- )
971- )
972- print (
973- "{:<40} {:<10d} ({:.2f}%)" .format (
974- "Reliable (TPOT>=1ms):" , metrics .n_decode_reliable , 100 * metrics .n_decode_reliable / _n_total
975- )
976- )
977- print ("{:<40} {:<10.2f}" .format ("Mean Decode (tok/s):" , metrics .s_decode_filtered_mean ))
978- print ("{:<40} {:<10.2f}" .format ("Median Decode (tok/s):" , metrics .s_decode_filtered_median ))
979- process_one_length ("s_decode" , "Decode" , "解码速度(tok/s)" )
946+ print ("{:<40} {:<10d}" .format ("Requests in decode stats:" , metrics .n_decode_total ))
947+ process_one_length ("s_decode" , "Decode" , "解码速度(tok/s, 首token之后)" )
980948 process_one_metric ("ttft" , "TTFT" , "Time to First Token" )
981949 process_one_metric ("s_ttft" , "S_TTFT" , "Infer Time to First Token" )
982950 process_one_metric ("res_ttft" , "Response TTFT" , "包含思考首token耗时" )
@@ -1164,22 +1132,8 @@ def process_one_length(
11641132 print ("{:<40} {:<10.2f}" .format (f"P{ p_word } { metric_name } :" , value ))
11651133 result [f"p{ p_word } _{ metric_attribute_name } " ] = value
11661134
1167- print ("{s:{c}^{n}}" .format (s = "解码速度 (过滤TPOT<1ms)" , n = 50 , c = "-" ))
1168- _n_total = max (metrics .n_decode_total , 1 )
1169- print ("{:<40} {:<10d}" .format ("Total requests:" , metrics .n_decode_total ))
1170- print (
1171- "{:<40} {:<10d} ({:.2f}%)" .format (
1172- "Filtered (TPOT<1ms):" , metrics .n_decode_filtered , 100 * metrics .n_decode_filtered / _n_total
1173- )
1174- )
1175- print (
1176- "{:<40} {:<10d} ({:.2f}%)" .format (
1177- "Reliable (TPOT>=1ms):" , metrics .n_decode_reliable , 100 * metrics .n_decode_reliable / _n_total
1178- )
1179- )
1180- print ("{:<40} {:<10.2f}" .format ("Mean Decode (tok/s):" , metrics .s_decode_filtered_mean ))
1181- print ("{:<40} {:<10.2f}" .format ("Median Decode (tok/s):" , metrics .s_decode_filtered_median ))
1182- process_one_length ("s_decode" , "Decode" , "解码速度(tok/s)" )
1135+ print ("{:<40} {:<10d}" .format ("Requests in decode stats:" , metrics .n_decode_total ))
1136+ process_one_length ("s_decode" , "Decode" , "解码速度(tok/s, 首token之后)" )
11831137 process_one_metric ("ttft" , "TTFT" , "Time to First Token" )
11841138 process_one_metric ("s_ttft" , "S_TTFT" , "Infer Time to First Token" )
11851139 process_one_metric ("tpot" , "TPOT" , "Time per Output Token (excl. 1st token)" )
0 commit comments