vLLM并发性能测试汇总:Qwen系列与Command R多模型推理延迟对比

硬件条件:8卡A100的80G显存机器

img_001

示例说明

执行脚本示例
bash qwen_default_performance.sh <input_len> <output_len> <tag> <model_path> <gpus> <qps>
bash qwen_default_performance.sh 8192 128 qwen2.5_32B /models/Qwen2.5-32B-Instruct/ “0,1,2,3,4,5,6,7” “1 2 4 8 16 32”
常用指标缩写

  • TTFT为首个token 响应时长 (Time to first token)
  • TPOT为每个 token 输出时长 (Time per output token)
  • ITL为跨 token 延迟 (Inter-token latency)
  • QPS为每秒请求数(Queries Per Second)
    ITL是衡量服务器每次处理请求并返回一定数量token所需的时间,而TPOT是衡量服务器处理单个token所需的时间。

    参数说明

    img_002
  • -backend vllm:使用 vLLM 作为后端。
  • -model Qwen2.5-32B-Instruct:使用指定的模型。
  • -dataset-path $dataset_path:数据集路径。
  • -sonnet-input-len 1024:每个请求的输入长度。
  • -sonnet-output-len 128:每个请求的输出长度。
  • -num-prompts 200:处理的提示数量。
  • -port 8996:服务端口。
  • -save-result:保存结果。
  • -result-dir $results_folder:结果目录。
  • -result-filename $result_filename:结果文件名。
  • -request-rate 8:请求速率,即 QPS(Queries Per Second)。

请求发送机制
请求发送机制取决于如何设置请求速率(QPS)。设置了 --request-rate 8,这意味着每秒发送 8 个请求。这是通过控制请求发送的频率来实现的,而不是等待所有请求处理完毕后再发送新的请求。这种发送机制有助于模拟真实的负载情况,其中请求是连续且均匀地到达的。
img_003

结果解释

根据您提供的运行结果,以下是对关键指标的解释:

  1. Successful requests: 成功处理的请求数量,这里是 200。
  2. Benchmark duration (s): 基准测试持续时间,这里是 57.00 秒。
  3. Total input tokens: 总输入标记数,这里是 202314。
  4. Total generated tokens: 总生成标记数,这里是 25600。
  5. Request throughput (req/s): 请求吞吐量,这里是 3.51 req/s,这表明每秒处理的请求数。
  6. Output token throughput (tok/s): 输出标记吞吐量,这里是 449.08 tok/s,这表明每秒生成的标记数。
  7. Total Token throughput (tok/s): 总标记吞吐量,这里是 3998.14 tok/s,这表明每秒处理的总标记数。

时间到第一个标记(Time to First Token, TTFT)

  • Mean TTFT (ms): 平均时间到第一个标记,这里是 10850.23 毫秒。
  • Median TTFT (ms): 中位数时间到第一个标记,这里是 10838.40 毫秒。
  • P99 TTFT (ms): 第 99 百分位时间到第一个标记,这里是 19837.84 毫秒。

每个输出标记的时间(Time per Output Token, TPOT)

  • Mean TPOT (ms): 平均每个输出标记的时间,这里是 265.20 毫秒。
  • Median TPOT (ms): 中位数每个输出标记的时间,这里是 276.84 毫秒。
  • P99 TPOT (ms): 第 99 百分位每个输出标记的时间,这里是 440.39 毫秒。

标记间延迟(Inter-token Latency, ITL)

  • Mean ITL (ms): 平均标记间延迟,这里是 265.00 毫秒。
  • Median ITL (ms): 中位数标记间延迟,这里是 106.96 毫秒。
  • P99 ITL (ms): 第 99 百分位标记间延迟,这里是 335.56 毫秒。

    Qwen2.5-32B-Instruct(TP=8)

    input=1024, output=128

    默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=8;
    默认情况:input_tokens=1024,output_tokens=128;

结论:input=1024, output=128情况下,并发约等于5(首token在5秒以内的并发数),每秒tokens生成个数为444.34(GPU计算100%时)
测试条件:200个请求,QPS=1,默认情况,平均首token为0.376s,平均每秒生成128.15个token
img_004
img_005

测试条件:200个请求,QPS=2,默认情况,平均首token为0.505s,平均每秒生成249.06个token
img_006
img_007

测试条件:200个请求,QPS=4,默认情况,平均首token为1.374s,平均每秒生成411.94个token
img_008
img_009

测试条件:200个请求,QPS=8,默认情况,平均首token为10.850s,平均每秒生成449.08个token
img_002
img_003

测试条件:200输入,QPS=16,默认情况,平均首token为17.530s,平均每秒生成447.10个token
img_010
img_011

测试条件:200输入,QPS=32,默认情况,平均首token为21.530s,平均每秒生成444.34个token
img_012
img_013

input=8192, output=128

默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=8;
默认情况:input_tokens=8192,output_tokens=128;
结论:input=8192, output=128, tp=8情况下,并发小于1(首token在5秒以内的并发数),每秒tokens生成个数为66.56(GPU计算100%时)
测试条件:200个请求,QPS=1,默认情况,平均首token为82.644s,平均每秒生成66.68个token
img_014
img_015

测试条件:200个请求,QPS=2,默认情况,平均首token为132.69s,平均每秒生成66.62个token
img_016
img_017

测试条件:200个请求,QPS=4,默认情况,平均首token为157.66s,平均每秒生成66.54个token
img_018
img_019

测试条件:200个请求,QPS=8,默认情况,平均首token为170.09s,平均每秒生成66.60个token
img_020
img_021

测试条件:200个请求,QPS=16,默认情况,平均首token为176.24s,平均每秒生成66.58个token
img_022
img_023

测试条件:200个请求,QPS=32,默认情况,平均首token为179.37s,平均每秒生成66.56个token
img_024
img_025

input=4096, output=128

默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=8;
默认情况:input_tokens=4096,output_tokens=128;
结论:input=4096, output=128情况下,并发约等于1(首token在5秒以内的并发数),每秒tokens生成个数为131.11(GPU计算100%时)
测试条件:200个请求,QPS=1,默认情况,平均首token为4.405s,平均每秒生成121.40个token
img_026
img_027

测试条件:200个请求,QPS=2,默认情况,平均首token为40.243s, 平均每秒生成131.24个token
img_028
img_029

测试条件:200个请求,QPS=4,默认情况,平均首token为65.404s, 平均每秒生成131.13个token
img_030
img_031

测试条件:200个请求,QPS=8,默认情况,平均首token为78.113s, 平均每秒生成131.07个token
img_032
img_033

测试条件:200个请求,QPS=16,默认情况,平均首token为84.201s, 平均每秒生成131.24个token
img_034
img_035

测试条件:200个请求,QPS=32,默认情况,平均首token为87.342s, 平均每秒生成131.11个token
img_036
img_037

input=2048, output=128

默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=8;
默认情况:input_tokens=2048,output_tokens=128;
结论:input=2048, output=128情况下,并发约等于2(首token在5秒以内的并发数),每秒tokens生成个数为244.71(GPU计算100%时)
测试条件:200个请求,QPS=1,默认情况,平均首token为0.919s, 平均每秒生成127.17个token
img_038
img_039

测试条件:200个请求,QPS=2,默认情况,平均首token为2.620s, 平均每秒生成227.70个token
img_040
img_041

测试条件:200个请求,QPS=4,默认情况,平均首token为21.057s, 平均每秒生成244.79个token
img_042
img_043

测试条件:200个请求,QPS=8,默认情况,平均首token为33.828s,平均每秒生成245.15个token
img_044
img_045

测试条件:200个请求,QPS=16,默认情况,平均首token为40.229s,平均每秒生成245.07个token
img_046
img_047

测试条件:200个请求,QPS=32,默认情况,平均首token为43.402s,平均每秒生成244.71个token
img_048
img_049

input=512, output=128

默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=8;
默认情况:input_tokens=512,output_tokens=128;
结论:input=512, output=128情况下,并发约等于10(首token在5秒以内的并发数),每秒tokens生成个数为723.53(GPU计算100%时)
测试条件:200个请求,QPS=1,默认情况,平均首token为0.192s,平均每秒生成128.30个token
img_050
img_051

测试条件:200个请求,QPS=2,默认情况,平均首token为0.236s,平均每秒生成250.47个token
img_052
img_053

测试条件:200个请求,QPS=4,默认情况,平均首token为0.323s,平均每秒生成467.53个token
img_054
img_055

测试条件:200个请求,QPS=8,默认情况,平均首token为0.850s,平均每秒生成668.15个token
img_056
img_057

测试条件:200个请求,QPS=16,默认情况,平均首token为6.371s,平均每秒生成725.57个token
img_058
img_059

测试条件:200个请求,QPS=32,默认情况,平均首token为9.912s,平均每秒生成723.53个token
img_060
img_061

input=256, output=128

默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=8;
默认情况:input_tokens=256,output_tokens=128;
结论:input=256, output=128情况下,并发约等于40(首token在5秒以内的并发数),每秒tokens生成个数为1086.13(GPU计算100%时)
测试条件:200个请求,QPS=1,默认情况,平均首token为0.127s,平均每秒生成127.95个token
img_062
img_063

测试条件:200个请求,QPS=2,默认情况下,平均首token为0.145s,平均每秒生成250.70个token
img_064
img_065

测试条件:200个请求,QPS=4,默认情况下,平均首token为0.175s,平均每秒生成473.93个token
img_066
img_067

测试条件:200个请求,QPS=8,默认情况下,平均首token为0.247s,平均每秒生成770.93个token
img_068
img_069

测试条件:200个请求,QPS=16,默认情,平均首token为0.563s,平均每秒生成1001.64个token
img_070
img_071

测试条件:200个请求,QPS=32,默认情况,平均首token为3.82s,平均每秒生成1078.37个token
img_072
img_073

测试条件:200个请求,QPS=48,默认情况,平均首token为5.169s,平均每秒生成1078.24个token
img_074
img_075

测试条件:200个请求,QPS=64,默认情况,平均首token为6.150s,平均每秒生成1086.13个token
img_076
img_077

input=1024, output=16

默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=8;
默认情况:input_tokens=1024,output_tokens=16;
结论:input=1024, output=16情况下,并发约等于5(首token在5秒以内的并发数),每秒tokens生成个数为79.91(GPU计算100%时),且TTFT和输出长度无关,减少输入长度,平均ITL和TPOT降低,但P99的ITL和TPOT增加。
测试条件:200个请求,QPS=1,默认情况,平均首token为0.370s,平均每秒生成16.20个token
img_078
img_079

测试条件:200个请求,QPS=2,默认情况,平均首token为0.512s,平均每秒生成32.20个token
img_080
img_081

测试条件:200个请求,QPS=4,默认情况,平均首token为1.392s,平均每秒生成62.37个token
img_082
img_083

测试条件:200个请求,QPS=8,默认情况,平均首token为11.024s,平均每秒生成71.06个token
img_084
img_085

测试条件:200个请求,QPS=16,默认情况,平均首token为17.704s,平均每秒生成71.16个token
img_086
img_087

测试条件:200个请求,QPS=32,默认情况,平均首token为21.109s,平均每秒生成70.91个token
img_088
img_089

input=512, output=16

默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=8;
默认情况:input_tokens=512,output_tokens=16;
结论:input=512, output=16情况下,并发约等于10(首token在5秒以内的并发数),每秒tokens生成个数为136.17(GPU计算100%时),且TTFT和输出长度无关,减少输入长度,平均ITL和TPOT降低,但P99的ITL和TPOT增加。
测试条件:200个请求,QPS=1,默认情况,平均首token为0.195s,平均每秒生成 16.22个token
img_090
img_091

测试条件:200个请求,QPS=2,默认情况,平均首token为0.224s,平均每秒生成 32.32个token
img_092
img_093

测试条件:200个请求,QPS=4,默认情况,平均首token为0.291s,平均每秒生成64.02个token
img_094
img_095

测试条件:200个请求,QPS=8,默认情况,平均首token为0.823s,平均每秒生成121.60个token
img_096
img_097

测试条件:200个请求,QPS=16,默认情况,平均首token为6.45s,平均每秒生成136.69个token
img_098
img_099
测试条件:200个请求,QPS=32,默认情况,平均首token为10.102s,平均每秒生成137.15个token
img_100
img_101

测试条件:200个请求,QPS=48,默认情况,平均首token为11.415s,平均每秒生成135.90个token
img_102
img_103

测试条件:200个请求,QPS=64,默认情况,平均首token为11.785s,平均每秒生成136.17个token
img_104
img_105

input=512, output=512

默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=8;
默认情况:input_tokens=512,output_tokens=512;
结论:input=512, output=512, tp=8情况下,并发约等于10(首token在5秒以内的并发数),每秒tokens生成个数为1129.47(GPU计算100%时)
测试条件:200个请求,QPS=1,默认情况,平均首token为0.213s,平均每秒生成367.80个token
img_106
img_107

测试条件:200个请求,QPS=2,默认情况,平均首token为0.254s,平均每秒生成638.05个token
img_108
img_109

测试条件:200个请求,QPS=4,默认情况,平均首token为0.350s,平均每秒生成919.32个token
img_110
img_111

测试条件:200个请求,QPS=8,默认情况,平均首token为0.806s,平均每秒生成1092.22个token
img_112
img_113

测试条件:200个请求,QPS=16,默认情况,平均首token为6.338s,平均每秒生成1138.77个token
img_114
img_115

测试条件:200个请求,QPS=32,默认情况,平均首token为9.845s,平均每秒生成1129.47个token
img_116
img_117

Qwen2.5-32B-Instruct(TP=4)

input=1024, output=128

默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=4;
默认情况:input_tokens=1024,output_tokens=128;
结论:input=1024, output=128, tp=4情况下,并发约等于6(首token在5秒以内的并发数),每秒tokens生成个数为538.46(GPU计算100%时)
测试条件:200个请求,QPS=1,默认情况,平均首token为0.292s,平均每秒生成128.09个token
img_118
img_119

测试条件:200个请求,QPS=2,默认情况,平均首token为0.372s,平均每秒生成249.57个token
img_120
img_121

测试条件:200个请求,QPS=4,默认情况,平均首token为0.700s,平均每秒生成452.29个token
img_122
img_123

测试条件:200个请求,QPS=8,默认情况,平均首token为5.757s,平均每秒生成537.40个token
img_124
img_125

测试条件:200个请求,QPS=16,默认情况,平均首token为12.295s,平均每秒生成536.93个token
img_126
img_127

测试条件:200个请求,QPS=32,默认情况,平均首token为15.634s,平均每秒生成538.46个token
img_128
img_129

input=8192, output=128

默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=4;
默认情况:input_tokens=8192,output_tokens=128;
结论:input=8192, output=128, tp=4情况下,并发小于1(首token在5秒以内的并发数),每秒tokens生成个数为77.09(GPU计算100%时)
测试条件:200个请求,QPS=1,默认情况,平均首token为54.515s,平均每秒生成78.64个token
img_130
img_131

测试条件:200个请求,QPS=2,默认情况,平均首token为108.015s,平均每秒生成76.97个token
img_132
img_133

测试条件:200个请求,QPS=4,默认情况,平均首token为133.744s,平均每秒生成76.65个token
img_134
img_135

测试条件:200个请求,QPS=8,默认情况,平均首token为145.913s,平均每秒生成76.79个token
img_136
img_137

测试条件:200个请求,QPS=16,默认情况,平均首token为151.851s,平均每秒生成76.93个token
img_138
img_139

测试条件:200个请求,QPS=32,默认情况,平均首token为154.779s,平均每秒生成77.09个token
img_140
img_141

Qwen2.5-32B-Instruct(TP=2)

input=1024, output=128

默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=2;
默认情况:input_tokens=1024,output_tokens=128;
结论:input=1024, output=128, tp=2情况下,并发约等于5(首token在5秒以内的并发数),每秒tokens生成个数为420.48(GPU计算100%时)
测试条件:200个请求,QPS=1,默认情况,平均首token为0.385s,平均每秒生成 127.23个token
img_142
img_143

测试条件:200个请求,QPS=2,默认情况,平均首token为0.545s,平均每秒生成246.91个token
img_144
img_145

测试条件:200个请求,QPS=4,默认情况,平均首token为1.623s,平均每秒生成398.82个token
img_146
img_147

测试条件:200个请求,QPS=8,默认情况,平均首token为12.111s,平均每秒生成419.83个token
img_148
img_149

测试条件:200个请求,QPS=16,默认情况,平均首token为18.726s,平均每秒生成420.87个token
img_150
img_151

测试条件:200个请求,QPS=32,默认情况,平均首token为21.984s,平均每秒生成420.48个token
img_152
img_153

多模型并发性能测试汇总

本节汇总 Command R、Qwen2.5-72B、Qwen1.5-72B/110B 等模型在 vLLM 下的并发性能测试结果。

Command R 08-2024在vLLM下的并发性能测试:TP=8/4/2多输入长度与QPS的TTFT/TPOT/ITL实测

vLLM 推理框架—Command R 08-2024 并发性能

硬件条件:8卡A100的80G显存机器
img_01

示例说明

执行脚本示例
bash cohere_performance.sh \<input_len\> \<output_len\> \<tag\> \<model_path\> \<gpus\> \<qps\>
bash cohere_performance.sh 1024 128 c4ai_32b /models/c4ai-command-r-08-2024/ "0,1,2,3,4,5,6,7" "1 2 4 8 16 32"
常用指标缩写

  • TTFT为首个token 响应时长 (Time to first token)
  • TPOT为每个 token 输出时长 (Time per output token)
  • ITL为跨 token 延迟 (Inter-token latency)
  • QPS为每秒请求数(Queries Per Second)
    ITL是衡量服务器每次处理请求并返回一定数量token所需的时间,而TPOT是衡量服务器处理单个token所需的时间。

    参数说明

    img_02
  • -backend vllm:使用 vLLM 作为后端。
  • -model c4ai-command-r-08-2024:使用指定的模型。
  • -dataset-path \$dataset_path:数据集路径。
  • -sonnet-input-len 1024:每个请求的输入长度。
  • -sonnet-output-len 128:每个请求的输出长度。
  • -num-prompts 200:处理的提示数量。
  • -port 8996:服务端口。
  • -save-result:保存结果。
  • -result-dir \$results_folder:结果目录。
  • -result-filename \$result_filename:结果文件名。
  • -request-rate 8:请求速率,即 QPS(Queries Per Second)。

请求发送机制
请求发送机制取决于如何设置请求速率(QPS)。设置了 --request-rate 8,这意味着每秒发送 8 个请求。这是通过控制请求发送的频率来实现的,而不是等待所有请求处理完毕后再发送新的请求。这种发送机制有助于模拟真实的负载情况,其中请求是连续且均匀地到达的。
img_03

结果解释

根据您提供的运行结果,以下是对关键指标的解释:

  1. Successful requests: 成功处理的请求数量,这里是 200。
  2. Benchmark duration (s): 基准测试持续时间,这里是 57.93 秒。
  3. Total input tokens: 总输入标记数,这里是 202386。
  4. Total generated tokens: 总生成标记数,这里是 25596。
  5. Request throughput (req/s): 请求吞吐量,这里是 3.45 req/s,这表明每秒处理的请求数。
  6. Output token throughput (tok/s): 输出标记吞吐量,这里是 441.87 tok/s,这表明每秒生成的标记数。
  7. Total Token throughput (tok/s): 总标记吞吐量,这里是 3935.70 tok/s,这表明每秒处理的总标记数。

时间到第一个标记(Time to First Token, TTFT)

  • Mean TTFT (ms): 平均时间到第一个标记,这里是 11016.46 毫秒。
  • Median TTFT (ms): 中位数时间到第一个标记,这里是 11005.07 毫秒。
  • P99 TTFT (ms): 第 99 百分位时间到第一个标记,这里是 20009.43 毫秒。

每个输出标记的时间(Time per Output Token, TPOT)

  • Mean TPOT (ms): 平均每个输出标记的时间,这里是 271.92 毫秒。
  • Median TPOT (ms): 中位数每个输出标记的时间,这里是 276.67 毫秒。
  • P99 TPOT (ms): 第 99 百分位每个输出标记的时间,这里是 447.46 毫秒。

标记间延迟(Inter-token Latency, ITL)

  • Mean ITL (ms): 平均标记间延迟,这里是 270.92 毫秒。
  • Median ITL (ms): 中位数标记间延迟,这里是 112.14 毫秒。
  • P99 ITL (ms): 第 99 百分位标记间延迟,这里是 355.09 毫秒。

常用指标缩写

  • TTFT为首个token 响应时长 (Time to first token)
  • TPOT为每个 token 输出时长 (Time per output token)
  • ITL为跨 token 延迟 (Inter-token latency)
  • QPS为每秒请求数(Queries Per Second)
    ITL是衡量服务器每次处理请求并返回一定数量token所需的时间,而TPOT是衡量服务器处理单个token所需的时间。

    c4ai-command-r-08-2024(TP=8)

    input=1024, output=128

    默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=8;
    默认情况:input_tokens=1024,output_tokens=128;

结论:input=1024, output=128情况下,并发约等于4(首token在5秒以内的并发数),每秒tokens生成个数为441.99(GPU计算100%时)
测试条件:200个请求,QPS=1,默认情况,平均首token为0.361s,平均每秒生成128.02个token
img_04
img_05

测试条件:200个请求,QPS=2,默认情况,平均首token为0.505s,平均每秒生成249.92个token
img_06
img_07

测试条件:200个请求,QPS=4,默认情况,平均首token为1.363s,平均每秒生成416.25个token
img_08
img_09

测试条件:200个请求,QPS=8,默认情况,平均首token为11.016s,平均每秒生成441.87个token
img_02
img_03

测试条件:200个请求,QPS=16,默认情况,平均首token为17.655s,平均每秒生成442.59个token
img_10
img_11

测试条件:200个请求,QPS=32,默认情况,平均首token为21.024s,平均每秒生成441.99个token
img_12
img_13

input=8192, output=128

默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=8;
默认情况:input_tokens=8192,output_tokens=128;
结论:input=8192, output=128情况下,并发小于1(首token在5秒以内的并发数),每秒tokens生成个数为67.31(GPU计算100%时)
测试条件:200个请求,QPS=1,默认情况,平均首token为80.999s,平均每秒生成67.42个token
img_14
img_15

测试条件:200个请求,QPS=2,默认情况,平均首token为130.893s,平均每秒生成67.31个token
img_16
img_17

测试条件:200个请求,QPS=4,默认情况,平均首token为155.893s,平均每秒生成67.31个token
img_18
img_19

测试条件:200个请求,QPS=8,默认情况,平均首token为168.318s,平均每秒生成67.25个token
img_20
img_21

测试条件:200个请求,QPS=16,默认情况,平均首token为174.566s,平均每秒生成67.32个token
img_22
img_23

测试条件:200个请求,QPS=32,默认情况,平均首token为177.576s,平均每秒生成67.35个token
img_24
img_25

input=2048, output=128

默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=8;
默认情况:input_tokens=8192,output_tokens=128;
结论:input=8192, output=128情况下,并发约等于2(首token在5秒以内的并发数),每秒tokens生成个数为246.94(GPU计算100%时)
测试条件:200个请求,QPS=1,默认情况,平均首token为0.955s,平均每秒生成127.34个token
img_26
img_27

测试条件:200个请求,QPS=2,默认情况,平均首token为2.542s,平均每秒生成231.27个token
img_28
img_29

测试条件:200个请求,QPS=4,默认情况,平均首token为20.797s,平均每秒生成246.89个token
img_30
img_31

测试条件:200个请求,QPS=8,默认情况,平均首token为33.542s,平均每秒生成246.76个token
img_32
img_33

测试条件:200个请求,QPS=16,默认情况,平均首token为39.907s,平均每秒生成246.93个token
img_34
img_35

测试条件:200个请求,QPS=32,默认情况,平均首token为43.091s,平均每秒生成246.94个token
img_36
img_37

c4ai-command-r-08-2024(TP=4)

input=1024, output=128

默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=4;
默认情况:input_tokens=1024,output_tokens=128;
结论:input=1024, output=128情况下,并发约等于6(首token在5秒以内的并发数),每秒tokens生成个数为530.96(GPU计算100%时)
测试条件:200个请求,QPS=1,默认情况,平均首token为0.290s,平均每秒生成128.11个token
img_38
img_39

测试条件:200个请求,QPS=2,默认情况,平均首token为0.374s,平均每秒生成250.70个token
img_40
img_41

测试条件:200个请求,QPS=4,默认情况,平均首token为0.705s,平均每秒生成458.69个token
img_42
img_43

测试条件:200个请求,QPS=8,默认情况,平均首token为5.945s,平均每秒生成530.33个token
img_44
img_45

测试条件:200个请求,QPS=16,默认情况,平均首token为12.621s,平均每秒生成530.21个token
img_46
img_47

测试条件:200个请求,QPS=32,默认情况,平均首token为15.941s,平均每秒生成530.96个token
img_48
img_49

c4ai-command-r-08-2024(TP=2)

input=1024, output=128

默认条件:vllm=0.6.0,gpu-memory-utilization=0.8,max_model_len=10000,tp=4;
默认情况:input_tokens=1024,output_tokens=128;
结论:input=1024, output=128情况下,并发约等于4(首token在5秒以内的并发数),每秒tokens生成个数为417.52(GPU计算100%时)
测试条件:200个请求,QPS=1,默认情况,平均首token为0.404s,平均每秒生成127.35个token
img_50
img_51

测试条件:200个请求,QPS=2,默认情况,平均首token为0.566s,平均每秒生成247.25个token
img_52
img_53

测试条件:200个请求,QPS=4,默认情况,平均首token为1.771s,平均每秒生成398.80个token
img_54
img_55

测试条件:200个请求,QPS=8,默认情况,平均首token为12.593s,平均每秒生成416.81个token
img_56
img_57

测试条件:200个请求,QPS=16,默认情况,平均首token为19.214s,平均每秒生成417.68个token
img_58
img_59

测试条件:200个请求,QPS=32,默认情况,平均首token为22.490s,平均每秒生成417.52个token
img_60
img_61

Qwen2.5-72B-Instruct并发性能测试:基于vLLM 0.7.3的QwQ-32B/Qwen2.5-72B/32B三模型8卡A100评测

vLLM 推理框架—Qwen2.5-72B-Instruct并发测试

vLLM 是一种高性能的 GPU 推理框架,通过 PagedAttention 技术优化 GPU 内存效率和吞吐量,适用于大规模高并发场景。

QwQ-32B

基于 llm_management_20250224:py311(vllm==0.7.3)首次部署

模型名称:QwQ-32B

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 创建容器(llm_management_20250224:py311中vllm==0.7.3)
sudo docker run --gpus all -it -d --network llm-management-network --name llm_management --ipc=host -P -p 8900-8997:8900-8997 -v /data/models:/models llm_management_20250224:py311 /bin/bash

# 开启v1加速
export VLLM_USE_V1=1
export VLLM_WORKER_MULTIPROC_METHOD=spawn

# 8卡启动QwQ-32B
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 python -m vllm.entrypoints.openai.api_server \
--served-model-name QwQ-32B \
--port 8996 \
--model /models/QwQ-32B \
--tensor-parallel-size 8 \
--max-model-len 16384 \
--gpu-memory-utilization 0.80 \
--disable-log-stats \
--api-key <YOUR_API_KEY>


# 测试脚本(feature_v2_sp3)
python test_qwq_batch_concurrent.py

QwQ-32B性能测试结果详情

img_01

QwQ-32B性能测试结果

img_02

img_03

Qwen2.5-72B-Instruct

基于 llm_management_20250224:py311(vllm==0.7.3)首次部署

模型名称:Qwen2.5-72B-Instruct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建容器(llm_management_20250224:py311中vllm==0.7.3)
sudo docker run --gpus all -it -d --network llm-management-network --name llm_management --ipc=host -P -p 8900-8997:8900-8997 -v /data/models:/models llm_management_20250224:py311 /bin/bash

# 开启v1加速
export VLLM_USE_V1=1
export VLLM_WORKER_MULTIPROC_METHOD=spawn

# 8卡启动Qwen2.5-72B-Instruct
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 python -m vllm.entrypoints.openai.api_server \
--served-model-name Qwen2.5-72B-Instruct \
--port 8996 \
--model /models/Qwen2.5-72B-Instruct \
--tensor-parallel-size 8 \
--max-model-len 16384 \
--gpu-memory-utilization 0.80 \
--disable-log-stats \
--api-key <YOUR_API_KEY>

Qwen2.5-72B-Instruct性能测试结果详情

img_04

Qwen2.5-72B-Instruct性能测试结果

img_05

img_06

Qwen2.5-32B-Instruct

基于 llm_management_20250224:py311(vllm==0.7.3)首次部署

模型名称:Qwen2.5-32B-Instruct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建容器(llm_management_20250224:py311中vllm==0.7.3)
sudo docker run --gpus all -it -d --network llm-management-network --name llm_management --ipc=host -P -p 8900-8997:8900-8997 -v /data/models:/models llm_management_20250224:py311 /bin/bash

# 开启v1加速
export VLLM_USE_V1=1
export VLLM_WORKER_MULTIPROC_METHOD=spawn

# 8卡启动Qwen2.5-32B-Instruct
CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 python -m vllm.entrypoints.openai.api_server \
--served-model-name Qwen2.5-32B-Instruct \
--port 8996 \
--model /models/Qwen2.5-32B-Instruct \
--tensor-parallel-size 8 \
--max-model-len 16384 \
--gpu-memory-utilization 0.80 \
--disable-log-stats \
--api-key <YOUR_API_KEY>

Qwen2.5-32B-Instruct性能测试结果详情

img_07

Qwen2.5-32B-Instruct性能测试结果

img_08

img_09

评测基准

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# 以vllm方式启用,openai client调用,测试qwq模型并发效果
import concurrent.futures
import os
import time
from datetime import datetime

import pandas as pd
from loguru import logger
from openai import OpenAI
from transformers import AutoTokenizer

from utils.generate_prompt_util import sample_sonnet_requests


# Initialize OpenAI client
client = OpenAI(
api_key="<YOUR_API_KEY>",
base_url="http://10.39.214.107:8996/v1",
)


# 初始化结果存储
results = []

def send_post_request(prompt, model, stream, max_tokens):
"""
发送POST请求
"""
response_start_time = datetime.now()
collected_messages = []
try:
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
temperature=0.7,
stream=stream,
max_tokens=max_tokens
)

if stream:
first_token_return_time = None
# 首token标志位
ttft_flag = True
for chunk in response:
if not chunk.choices:
continue
chunk_message = chunk.choices[0].delta
if not chunk_message:
continue

if chunk_message.content is not None:
if ttft_flag:
logger.info(chunk_message.content)
first_token_return_time = datetime.now()
logger.info(f"First token return time: {first_token_return_time - response_start_time}")
ttft_flag = False
collected_messages.append(chunk_message.content)

response_end_time = datetime.now()
logger.info(f"Response running time: {response_end_time - response_start_time}")
logger.info(f"Collected messages: {''.join(collected_messages)}")

return {
"first_token_time": (first_token_return_time - response_start_time).total_seconds() if first_token_return_time else None,
"total_time": (response_end_time - response_start_time).total_seconds()
}
except Exception as e:
logger.error(f"Exception: {e}")
return {"first_token_time": None, "total_time": None}


def ensure_directory_exists(directory):
"""
确保目录存在,如果不存在则创建它

Args:
directory (str): 要确保存在的目录路径
"""
if not os.path.exists(directory):
os.makedirs(directory)


def run_concurrent_requests(backend, num_requests, input_length, model, stream, max_tokens, dataset_path, tokenizer):
"""
执行并发请求,并发地向指定的后端发送请求,并计算相关性能指标。

Args:
backend (str): 后端服务名,用于区分不同的后端环境。
num_requests (int): 并发请求的数量。
input_length (int): 每个请求的输入长度。
model (str): 用于生成响应的模型名。
stream (bool): 是否以流的方式接收响应。
max_tokens (int): 每个响应的最大token数。
dataset_path (str): 数据集路径,从中采样请求输入。
tokenizer (obj): 用于文本分词的tokenizer对象。

Returns:
tuple: 包含以下四个元素的元组:
request_success_rate (float): 请求成功率,成功完成的请求数占总请求数的比例。
avg_first_token_time (float): 平均首token返回时间,单位秒,表示从请求发送到收到第一个token的平均时间。
avg_total_time (float): 平均完成对话时间,单位秒,表示从请求发送到收到完整响应的平均时间。
avg_token_generation_speed (float): 平均token生成速度,单位tokens/s,表示每秒生成的token数。
"""
logger.info(f"Running {num_requests} concurrent requests with input length {input_length}...")
input_requests = sample_sonnet_requests(
dataset_path=dataset_path,
num_requests=num_requests,
input_len=input_length,
output_len=max_tokens,
prefix_len=50,
tokenizer=tokenizer
)
test_prompts = [prompt_formatted for prompt, prompt_formatted, prompt_len, output_len in input_requests]

with concurrent.futures.ThreadPoolExecutor(max_workers=num_requests) as executor:
futures = [executor.submit(send_post_request, prompt, model, stream, max_tokens) for prompt in test_prompts]
results = [future.result() for future in concurrent.futures.as_completed(futures)]

first_token_times = [result["first_token_time"] for result in results if result["first_token_time"] is not None]
total_times = [result["total_time"] for result in results if result["total_time"] is not None]

avg_first_token_time = sum(first_token_times) / len(first_token_times) if first_token_times else None
avg_total_time = sum(total_times) / len(total_times) if total_times else None
avg_token_generation_speed = max_tokens / (avg_total_time - avg_first_token_time) if avg_total_time - avg_first_token_time else None
request_success_rate = len(total_times) / num_requests if num_requests else None

# Ensure the results directory exists
results_dir = f"./results/{backend}"
ensure_directory_exists(results_dir)

# Data storage
with open(
f"{results_dir}/{backend}_{model}_{num_requests}_requests_{input_length}_{max_tokens}_tokens.txt",
"w", encoding="utf-8") as f:
# 总体数据
f.write(f"请求数: {num_requests}\n")
f.write(f"平均首token返回时间: {avg_first_token_time}\n")
f.write(f"平均完成对话时间: {avg_total_time}\n")
f.write(f"平均token生成速度: {avg_token_generation_speed}\n\n\n")

# 请求数据
if num_requests >= 5:
# 前5个请求的ttft
ttft_data = first_token_times[: 5]
ttft_str = ", ".join(map(str, ttft_data))
f.write(f"前五个请求的首token返回时间为:\n {ttft_str}\n")
# 前5个请求的完成时间
response_end_time_data = total_times[: 5]
response_end_time_str = ", ".join(map(str, response_end_time_data))
f.write(f"前五个请求的完成时间为:\n {response_end_time_str}\n")
# 所有请求数据
ttft_data = first_token_times
ttft_str = ", ".join(map(str, ttft_data))
f.write(f"所有请求的首token返回时间为:\n {ttft_str}\n")
# 前5个请求的完成时间
response_end_time_data = total_times
response_end_time_str = ", ".join(map(str, response_end_time_data))
f.write(f"所有请求的完成时间为:\n {response_end_time_str}\n")


if __name__ == "__main__":
# 定义测试参数
# 使用框架
test_backend = 'vllm' # sglang, lmdeploy
test_concurrent_levels = [1, 2, 4, 8, 16, 32, 64, 128, 256] # Concurrency levels
test_input_lengths = [256, 512, 1024, 2048, 4096, 8192, 16000] # Input lengths
test_output_lengths = [128] # Output lengths
test_model_name = "QwQ-32B" # Model name
test_stream = True # Stream output
test_tokenizer_dir = "./models/QwQ-32B" # Tokenizer path
test_dataset_path = "./datasets/sonnet_4x.txt" # Dataset path

# 初始化Tokenizer
test_tokenizer = AutoTokenizer.from_pretrained(test_tokenizer_dir, trust_remote_code=True)

for test_output_length in test_output_lengths:
for test_concurrent_level in test_concurrent_levels:
for test_input_length in test_input_lengths:
test_request_success_rate, test_avg_first_token_time, test_avg_total_time, test_avg_token_generation_speed = run_concurrent_requests(
backend=test_backend,
num_requests=test_concurrent_level,
input_length=test_input_length,
model=test_model_name,
stream=test_stream,
max_tokens=test_output_length,
dataset_path=test_dataset_path,
tokenizer=test_tokenizer
)
results.append({
"Concurrency": test_concurrent_level,
"Input Length": test_input_length,
"Output Length": test_output_length,
"Request Success Rate (%)": f"{test_request_success_rate * 100:.1f}%",
"Avg First Token Time (s)": f"{test_avg_first_token_time:.6f}",
"Avg Total Time (s)": f"{test_avg_total_time:.6f}",
"Avg Token Generation Speed (token/s)": f"{test_avg_token_generation_speed:.6f}",
"Output Throughput(token/s)": f"{test_concurrent_level * test_avg_token_generation_speed:.6f}"
})

# 每次执行完一个并发数后暂停10秒
logger.info(f"Completed {test_concurrent_level} concurrent requests. Pausing for 10 seconds...")
time.sleep(10)


# 将结果保存到Excel文件
df = pd.DataFrame(results)
df.to_excel(f"./results/{test_backend}/{test_backend}_{test_model_name}.xlsx", index=False)
logger.info("Test results saved to test_results.xlsx")

Qwen1.5-72B/110B并发性能测试:vLLM与私有化封装对比及ChatGLM2/3-6B部署结论

vLLM 推理框架—Qwen1.5并发性能测试

Qwen1.5系列

prompt_200prompt_200 = """总结提炼以下文字50字左右:在数字化时代,信息的获取和处理变得尤为重要。作为一款智能助手,我致力于帮助用户高效地处理和理解各种信息。无论是文本、表格还是演示文档,我都能够迅速阅读和分析,提供精准的信息反馈。 在这个过程中,我始终遵循用户的需求,努力提供丰富、详尽且有帮助的回答。同时,我也严格遵守法律法规,确保服务的安全性和合规性,坚决不涉及不当内容。我深知,作为智能助手,我的任务是为用户提供支持和便利。"""
prompt_512prompt_512 = """总结提炼以下文字120字左右:在这个信息爆炸的时代,人们对于获取信息的途径和方式有着更高的要求。随着科技的不断进步,人工智能技术已经成为了我们日常生活中不可或缺的一部分。作为一款由月之暗面科技有限公司开发的智能助手,我被赋予了强大的功能和独特的优势,旨在为用户提供更加便捷、高效的信息服务。 首先,我能够处理和理解多种格式的文件,包括TXT、PDF、Word文档、PPT幻灯片和Excel电子表格等。这意味着用户可以轻松地将他们需要的信息以文件的形式发送给我,而我则能够迅速地阅读和分析这些内容,从而为用户提供他们所需的信息。无论是进行数据分析、提取关键信息还是解答具体问题,我都能够提供准确且及时的反馈。 此外,我还具备访问互联网的能力,这让我能够获取最新的信息和数据,以便为用户提供最准确的答案。无论是时事新闻、科技动态还是学术研究,我都能够通过搜索功能,快速地找到相关的信息,并结合用户的问题给出详尽的回答。 我始终遵循着用户的指令,尽我所能地去完成用户的指令。我理解到,用户的需求是多样化的,因此我始终致力于提供丰富、详尽且有帮助的回答。同时,我也意识到,作为一个智能助手,我必须遵守相关的法律法规和社会道德标准。"""
prompt_2000prompt_2000 = """大模型,全称为大规模预训练模型,是人工智能领域中的一种重要技术,尤其在自然语言处理(NLP)和计算机视觉(CV)等方向有着广泛的应用。大模型的出现,极大地推动了AI技术的发展,使得机器能够更好地理解和生成人类语言,甚至在某些任务上超越了人类的表现。下面,我将从大模型的定义、发展历程、工作原理、应用领域以及未来趋势等方面进行详细介绍。

一、大模型的定义

大模型,顾名思义,是指模型参数量非常大的机器学习模型。这些模型通常由数十亿甚至数百亿个参数组成,远超传统的人工智能模型。这些参数是在大量的无标注数据上通过预训练得到的,目的是学习到数据中的通用知识和模式,以便在后续的特定任务上进行微调,从而达到更好的性能。

二、大模型的发展历程

  1. 早期阶段:在深度学习兴起之前,人工智能模型的规模相对较小,如SVM、决策树等。随着深度学习的出现,模型的规模开始增大,如AlexNet、VGG、ResNet等在计算机视觉领域的模型,以及RNN、LSTM等在自然语言处理领域的模型。

  2. 预训练模型阶段:2018年,Google推出了BERT(Bidirectional Encoder Representations from Transformers),这是一个基于Transformer架构的预训练模型,其规模已经达到了数亿参数。BERT的出现,开启了预训练模型的新篇章,它在多项NLP任务上取得了显著的性能提升。

  3. 大规模预训练模型阶段:2020年,OpenAI发布了GPT-3(Generative Pre-training Transformer 3),模型参数量达到了1750亿,这是当时最大的预训练模型。随后,阿里云的Qwen、百度的ERNIE、华为的鹏城-盘古、阿里云的M6等国内大厂也纷纷推出自己的大模型,参数量不断刷新纪录。

三、大模型的工作原理

大模型的核心是Transformer架构,这是一种自注意力机制,可以并行处理输入序列,解决了RNN等模型的序列依赖问题,大大提高了训练效率。在预训练阶段,大模型通过无监督学习的方式,如 masked language modeling 和 next sentence prediction 等任务,学习语言的内在规律和模式。在微调阶段,根据具体任务,如文本分类、问答系统、机器翻译等,对预训练模型进行针对性的训练,使其适应特定场景。

四、大模型的应用领域

  1. 自然语言处理:大模型在文本生成、问答系统、情感分析、机器翻译、对话系统等领域有广泛应用。例如,GPT-3可以生成连贯、有逻辑的文本,甚至可以进行简单的编程。

  2. 计算机视觉:大模型也可以应用于图像识别、目标检测、图像生成等任务,如ViT(Vision Transformer)就是将Transformer应用于CV领域的成功案例。

  3. 跨模态学习:大模型可以同时理解文本和图像信息,实现跨模态的交互和理解,如CLIP(Contrastive Language-Image Pre-training)模型。

  4. 其他领域:大模型还可以应用于语音识别、推荐系统、生物信息学、物理模拟等多个领域。

五、大模型的挑战与未来趋势

尽管大模型带来了显著的性能提升,但也面临着一些挑战,如计算资源需求巨大、训练和推理效率低、泛化能力有限、可解释性差等。因此,未来的趋势可能会是:

  1. 模型压缩与量化:通过模型剪枝、知识蒸馏等方法,减少模型大小,提高推理效率。

  2. 能效优化:研发更高效的硬件和算法,降低大模型的能耗。

  3. 强化学习与元学习:通过强化学习和元学习,使模型能更快地适应新任务,提高泛化能力。

  4. 可解释性研究:提高模型的可解释性,增强人机交互的信任度。

  5. 多模态与跨模态学习:结合不同类型的输入信息,提升模型的理解和生成能力。

总的来说,大模型是人工智能发展的重要里程碑,它不仅推动了技术的进步,也为我们的生活带来了诸多便利。随着技术的不断迭代和优化,我们有理由相信,大模型将在未来发挥更大的作用,为人类社会创造更多的价值。”””

Qwen1.5-72B-Chat

4卡,vllm,4096最大长度

启动方式

1
CUDA_VISIBLE_DEVICES=1,2,3,4 python -m vllm.entrypoints.openai.api_server --served-model-name Qwen1.5-72B-Chat --port 8991  --model /models/Qwen1.5-72B-Chat --tensor-parallel-size 4 --max-model-len 4096 --gpu-memory-utilization 0.75

4卡,4096最大长度,vllm,2000输入,512输出,并发分别为1,40,256

1、测试结果(2000输入,512输出,1并发),时间单位/秒,22.8 tokens/s

  • 前五个请求的第一个token返回时间:0.574028
  • response_start_time_average: 0.574028
  • 前五个请求的最后一个token返回时间:13.375516
  • response_end_time_average: 13.375516

2、测试结果(2000输入,512输出,40并发),时间单位/秒

  • 前五个请求的第一个token返回时间:1.063959, 1.082008, 2.742584, 2.750586, 2.76558
  • response_start_time_average: 11.415306750000001
  • 前五个请求的最后一个token返回时间:46.19368, 47.478858, 47.488864, 47.599811, 50.408245
  • response_end_time_average: 58.81113555

3、测试结果(2000输入,512输出,256并发),时间单位/秒

  • 前五个请求的第一个token返回时间:0.978002, 1.022001, 2.725005, 2.777001, 2.788007
  • response_start_time_average: 176.60942325390624
  • 前五个请求的最后一个token返回时间:42.569945, 43.109365, 43.950199, 44.987483, 47.045394
  • response_end_time_average: 225.57635427734368

4卡,vllm,32768最大长度

启动方式

1
CUDA_VISIBLE_DEVICES=1,2,3,4 python -m vllm.entrypoints.openai.api_server --served-model-name Qwen1.5-72B-Chat --port 8991  --model /models/Qwen1.5-72B-Chat --tensor-parallel-size 4 --max-model-len 32768 --gpu-memory-utilization 0.75

4卡,32768最大长度,vllm,2000输入,512输出,并发分别为1,40,256

1、测试结果(2000输入,512输出,1并发),时间单位/秒,22.2 tokens/s

  • 前五个请求的第一个token返回时间:0.599894
  • response_start_time_average: 0.599894
  • 前五个请求的最后一个token返回时间:14.133368
  • response_end_time_average: 14.133368

2、测试结果(2000输入,512输出,40并发),时间单位/秒,前三条速度很快

  • 前五个请求的第一个token返回时间:1.450993, 1.457002, 1.462003, 14.920011, 14.921026
  • response_start_time_average: 18.555341225
  • 前五个请求的最后一个token返回时间:43.130326, 43.76127, 44.728032, 45.359908, 45.954806
  • response_end_time_average: 58.34559765

3、测试结果(2000输入,512输出,256并发),时间单位/秒,前一条速度很快

  • 前五个请求的第一个token返回时间:0.570004, 14.766113, 14.812125, 14.818117, 14.825118
  • response_start_time_average: 185.39021865234378
  • 前五个请求的最后一个token返回时间:39.982682, 40.008683, 41.408154, 41.464129, 41.47612
  • response_end_time_average: 230.58626417968762

结论:官方vllm的max_model_len不影响单并发、256并发,40并发耗时提升约50%。


4卡,私有化封装,4096最大长度

启动方式

1
CUDA_VISIBLE_DEVICES=1,2,3,4 python ./service/foundation_model/qwen_chat.py --port 8991  --model /models/Qwen1.5-72B-Chat --tensor_parallel_size 4 --max_model_len 4096 --gpu_memory_utilization 0.75

4卡,4096最大长度,vllm,2000输入,512输出,并发分别为1,40,256

1、测试结果(2000输入,512输出,1并发),时间单位/秒,24.2 tokens/s

  • 第一个请求的第一个token返回时间:0.882857
  • response_start_time_average: 0.882857
  • 第一个请求的最后一个token返回时间:13.175237
  • response_end_time_average: 13.175237

2、测试结果(2000输入,512输出,40并发),时间单位/秒

  • 前五个请求的第一个token返回时间:15.693883, 15.658891, 15.636887, 15.717891, 15.641887
  • response_start_time_average: 22.158714125000003
  • 前五个请求的最后一个token返回时间:38.990434, 42.371323, 48.083964, 49.15638, 50.4055
  • response_end_time_average: 69.001847975

3、测试结果(2000输入,512输出,256并发),时间单位/秒

  • 前五个请求的第一个token返回时间:15.8075, 15.815497, 15.804504, 15.800505, 15.841502
  • response_start_time_average: 243.73916464062512
  • 前五个请求的最后一个token返回时间:39.602129, 44.075711, 50.699566, 51.167524, 52.672676
  • response_end_time_average: 302.3232604140626

4卡,私有化封装,32768最大长度

启动方式

1
CUDA_VISIBLE_DEVICES=1,2,3,4 python ./service/foundation_model/qwen_chat.py --port 8991  --model /models/Qwen1.5-72B-Chat --tensor_parallel_size 4 --max_model_len 32768 --gpu_memory_utilization 0.75

4卡,32768最大长度,vllm,2000输入,512输出,并发分别为1,40,256

1、测试结果(2000输入,512输出,1并发),时间单位/秒

  • 第一个请求的第一个token返回时间:0.922553
  • response_start_time_average: 0.922553
  • 第一个请求的最后一个token返回时间:13.325315
  • response_end_time_average: 13.325315

2、测试结果(2000输入,512输出,40并发)

  • 前五个请求的第一个token返回时间:15.799517, 15.808511, 15.864479, 15.889488, 15.886475
  • response_start_time_average: 22.247778274999995
  • 前五个请求的最后一个token返回时间:42.416026, 43.562881, 47.233508, 50.159447, 51.210282
  • response_end_time_average: 68.37788262499998

3、测试结果(2000输入,512输出,256并发)

  • 前五个请求的第一个token返回时间:15.854161, 15.808154, 15.794163, 15.827147, 15.81914
  • response_start_time_average: 247.9474947460937
  • 前五个请求的最后一个token返回时间:39.903629, 39.911628, 44.407305, 45.926388, 48.999993
  • response_end_time_average: 307.88330739843764

结论:私有化封装的max_model_len不影响模型第一个token时间和总返回时间

Qwen1.5-110B-Chat

4卡,vllm,4096最大长度

启动方式

1
CUDA_VISIBLE_DEVICES=1,2,3,4 python -m vllm.entrypoints.openai.api_server --served-model-name Qwen1.5-110B-Chat --port 8991  --model /models/Qwen1.5-110B-Chat --tensor-parallel-size 4 --max-model-len 4096 --gpu-memory-utilization 0.77

4卡,4096最大长度,vllm,2000输入,512输出,并发分别为1,40,256

1、测试结果(2000输入,512输出,1并发),时间单位/秒, 19.2 tokens/s

  • 前五个请求的第一个token返回时间:0.74063
  • response_start_time_average: 0.74063
  • 前五个请求的最后一个token返回时间:11.913654
  • response_end_time_average: 11.913654

2、测试结果(2000输入,512输出,40并发),时间单位/秒

  • 前五个请求的第一个token返回时间:1.199546, 1.208588, 3.15569, 3.185673, 3.198662
  • response_start_time_average: 11.030676525
  • 前五个请求的最后一个token返回时间:32.920417, 32.989418, 33.024932, 33.081456, 33.1034
  • response_end_time_average: 38.37808929999999

3、测试结果(2000输入,512输出,256并发),时间单位/秒

  • 前五个请求的第一个token返回时间:1.654987, 1.66899, 1.673998, 3.627005, 3.637012
  • response_start_time_average: 102.93876760546877
  • 前五个请求的最后一个token返回时间:56.828779, 57.16778, 57.170783, 57.268787, 57.28578
  • response_end_time_average: 163.7370169765626

4卡,vllm,32768最大长度

启动方式

1
CUDA_VISIBLE_DEVICES=1,2,3,4 python -m vllm.entrypoints.openai.api_server --served-model-name Qwen1.5-110B-Chat --port 8991  --model /models/Qwen1.5-110B-Chat --tensor-parallel-size 4 --max-model-len 32768 --gpu-memory-utilization 0.77

4卡,32768最大长度,vllm,2000输入,512输出,并发分别为1,40,256

1、测试结果(2000输入,512输出,1并发),时间单位/秒, 19.2 tokens/s

  • 前五个请求的第一个token返回时间:0.619865
  • response_start_time_average: 0.619865
  • 前五个请求的最后一个token返回时间:11.570689
  • response_end_time_average: 11.570689

2、测试结果(2000输入,512输出,40并发),时间单位/秒

  • 前五个请求的第一个token返回时间:2.586619, 2.592628, 2.628583, 2.638603, 2.64161
  • response_start_time_average: 17.967369324999996
  • 前五个请求的最后一个token返回时间:33.563753, 33.593744, 33.645755, 35.529838, 36.181372
  • response_end_time_average: 43.930624974999986

3、测试结果(2000输入,512输出,256并发),时间单位/秒

  • 前五个请求的第一个token返回时间:1.643004, 1.650005, 1.656999, 19.021139, 19.027133
  • response_start_time_average: 138.04490729687504
  • 前五个请求的最后一个token返回时间:33.551216, 34.284966, 34.350958, 35.618316, 35.664315
  • response_end_time_average: 176.71878233984376

结论:官方vllm的max_model_len不影响单并发、256并发,40并发耗时提升约50%。


4卡,私有化封装,4096最大长度

启动方式

1
CUDA_VISIBLE_DEVICES=1,2,3,4 python ./service/foundation_model/qwen_chat.py --port 8991  --model /models/Qwen1.5-110B-Chat --tensor_parallel_size 4 --max_model_len 4096 --gpu_memory_utilization 0.77

4卡,4096最大长度,vllm,2000输入,512输出,并发分别为1,40,256

1、测试结果(2000输入,512输出,1并发),时间单位/秒,18.1 tokens/s

  • 第一个请求的第一个token返回时间:0.960181
  • response_start_time_average: 0.960181
  • 第一个请求的最后一个token返回时间:12.841381
  • response_end_time_average: 12.841381

2、测试结果(2000输入,512输出,40并发),时间单位/秒

  • 前五个请求的第一个token返回时间:20.951468, 20.975463, 20.944461, 20.919458, 20.95246
  • response_start_time_average: 20.96528225
  • 前五个请求的最后一个token返回时间:50.763612, 50.841614, 51.566975, 52.587483, 52.943488
  • response_end_time_average: 59.514841925000006

3、测试结果(2000输入,512输出,256并发),时间单位/秒

  • 前五个请求的第一个token返回时间:41.775744, 41.709742, 41.854731, 41.972718
  • response_start_time_average: 161.60969656250018
  • 前五个请求的最后一个token返回时间:75.101425, 80.340986, 85.438611, 85.318608, 85.582627
  • response_end_time_average: 253.64892319531225

4卡,私有化封装,32768最大长度

启动方式

1
CUDA_VISIBLE_DEVICES=1,2,3,4 python ./service/foundation_model/qwen_chat.py --port 8991  --model /models/Qwen1.5-110B-Chat --tensor_parallel_size 4 --max_model_len 32768 --gpu_memory_utilization 0.77

4卡,32768最大长度,vllm,2000输入,512输出,并发分别为1,40,256

1、测试结果(2000输入,512输出,1并发),时间单位/秒,18.4 tokens/s

  • 第一个请求的第一个token返回时间:1.003635
  • response_start_time_average: 1.003635
  • 第一个请求的最后一个token返回时间:14.044728
  • response_end_time_average: 14.044728

2、测试结果(2000输入,512输出,40并发)

  • 前五个请求的第一个token返回时间:21.403267, 21.374292, 21.389297, 21.437257, 21.37728
  • response_start_time_average: 21.403976375000003
  • 前五个请求的最后一个token返回时间:42.717535, 48.127041, 51.04268, 51.058684, 53.334163
  • response_end_time_average: 62.728350625

3、测试结果(2000输入,512输出,256并发)

  • 前五个请求的第一个token返回时间:21.36488, 21.360869, 21.444868, 21.343875, 21.427845
  • response_start_time_average: 206.9659326757812
  • 前五个请求的最后一个token返回时间:48.675407, 50.051908, 50.1279, 51.756788, 54.681915
  • response_end_time_average: 264.7068086835935

结论:私有化封装的max_model_len不影响模型第一个token时间和总返回时间

ChatGLM

ChatGLM2-6B

1卡,HF,8192最大长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# -*- coding: utf-8 -*-
# @Time : 2023/9/6
# @Author : gkm0120
# @File : test_glm2_api.py
# @Software: PyCharm
import json
import os
import sys

import uvicorn
from fastapi import FastAPI
from loguru import logger
from torch.cuda import empty_cache, ipc_collect, is_available
from transformers import AutoTokenizer, AutoModel

project_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
sys.path.insert(0, project_path)

from config.message import ChatRequest
from config.paths import chatglm2_6b_model_path
from utils.time_util import time_it
from utils.multi_gpus_util import load_model_on_gpus


def load_model(model_path):
"""
多GPU策略加载模型
:param model_path:
:return:
"""
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
if is_available():
logger.info("loading chat model on GPU")
model = load_model_on_gpus(model_path, num_gpus=3)
logger.info("GPU加载模型完毕")
else:
logger.info("loading chat model on CPU")
model = AutoModel.from_pretrained(model_path, trust_remote_code=True).float()
logger.info("CPU加载模型完毕")
model.eval()
return tokenizer, model


class Chat:
@time_it
def __init__(self):
pass

@time_it
def answer(self, conversation, tokenizer, model, history, temperature, max_length, top_p):
"""
:param top_p: 解码阶段的解码策略。根据候选项可能性得分之和达到阈值p,来选择候选项的个数
:param max_length: 生成文本token最大长度
:param temperature: 模型预测的字符概率。temperature 越大,则新的概率分布越均匀,随机性也就越大
:param history: 历史记录
:param conversation: 用户查询语句query
:param tokenizer: 分词器。如chatglm2-6b使用SentencePiece方法
:param model: 语言模型,如chatglm2-6b
:return:
"""
response, history = model.chat(tokenizer,
conversation,
temperature=temperature,
max_length=max_length,
top_p=top_p,
history=history)
# 释放显存
empty_cache()
ipc_collect()
return response

@time_it
def answer_generator(self, conversation, history, tokenizer, model):
"""
:param conversation:
:param history:
:param tokenizer:
:param model:
:return:
"""
streams = model.stream_chat(tokenizer,
conversation,
history=history)
return streams


# 初始化fastapi
chat_app = FastAPI()

# 初始化云谷小智
# os.environ['CUDA_VISIBLE_DEVICES'] = '3,4,5'
zoomlion_tokenizer, zoomlion_model = load_model(chatglm2_6b_model_path)
zoomlion_chat = Chat()

logger.info(zoomlion_chat.answer(conversation="你是谁啊",
tokenizer=zoomlion_tokenizer,
model=zoomlion_model,
history=[],
temperature=0.8,
max_length=8192,
top_p=0.8))


@chat_app.post("/chat/")
def chat(request: ChatRequest):
ans = None
state = True
try:
request = json.loads(request.json())
prompt = request.get("prompt")
temperature = request.get("temperature")
history = request.get("history")
max_length = request.get("max_length")
top_p = request.get("top_p")
logger.info("Receive message: {} ".format(prompt))

ans = zoomlion_chat.answer(conversation=prompt,
tokenizer=zoomlion_tokenizer,
model=zoomlion_model,
history=history,
temperature=temperature,
max_length=max_length,
top_p=top_p)
logger.info("Sending message: {}".format(ans))
except Exception as e:
state = False
logger.error(e)
return {
"state": state,
"response": ans
}


def start_chat_service(args):
"""
:param args:
:return:
"""
uvicorn.run("test_glm2_api:chat_app",
host=args.host,
port=int(args.port),
workers=int(args.workers),
debug=True)


if __name__ == "__main__":
"""
nohup python test_glm2_api.py &
"""
# "不要使用test_glm2_api:chat_app,否则源文件会再执行一遍"
# http://10.103.190.9:9001/chat
uvicorn.run(app="test_glm2_api:chat_app",
host="0.0.0.0",
port=9001,
workers=2)

1卡,vllm,8192最大长度

chatglm2-6b 100个请求并发测试(调用接口)
测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import concurrent.futures
import json
from datetime import datetime

import requests

# 目标 POST 接口的 URL
url = "http://10.103.190.9:9001/chat"
# 要发送的 POST 数据
data = json.dumps({
"prompt": "晚上睡不着应该怎么办",
"temperature": 0.8,
"history": [],
"max_length": 8192,
"top_p": 0.8
})

headers = {
'Content-Type': 'application/json'
}


# 定义一个函数,用于发送 POST 请求
def send_post_request(url, data):
try:
response = requests.post(url, headers=headers, data=data)
print(response.text)
# 如果需要,你可以在这里处理响应内容
# 例如,打印响应状态码:print(response.status_code)
except Exception as e:
# 处理请求过程中的异常
print(f"Exception: {e}")


# 定义一个函数,用于执行并发任务
def run_concurrent_requests(num_requests):
start_time = datetime.now()
with concurrent.futures.ThreadPoolExecutor() as executor:
# 启动并发任务
futures = [executor.submit(send_post_request, url, data) for _ in range(num_requests)]
# 等待所有任务完成
concurrent.futures.wait(futures)

end_time = datetime.now()
print("end_time - start_time: {}".format(end_time - start_time))


if __name__ == "__main__":
# 指定并发请求数量
num_concurrent_requests = 100
# 运行并发请求
run_concurrent_requests(num_concurrent_requests)

测试结果:发送100个请求后,1236秒后请求全部结束,后台日志截图如下:

测试结果日志

chatglm2-6b 100个请求并发测试(vllm部署)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from datetime import datetime
from vllm import LLM, SamplingParams


def build_prompt(prompt_):
return "[Round 1]\n\n问:{}\n\n答:".format(prompt_)


prompts = [
"晚上睡不着应该怎么办"
] * 100
prompts = [build_prompt(item) for item in prompts]
# edit this model_url
model_url = '/mnt/lustre/share/test_train/young/knowledge_extraction/models/chatglm2-6b'

sampling_params = SamplingParams(temperature=0.8, top_p=0.95, max_tokens=8192)

llm = LLM(model=model_url,
tensor_parallel_size=1,
trust_remote_control=True,
dtype="float16",
gpu_memory_utilization=0.6)
start_time = datetime.now()
outputs = llm.generate(prompts, sampling_params)

# Print the outputs.
for output in outputs:
prompt = output.prompt
generated_text = output.outputs[0].text
# print(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")
end_time = datetime.now()
print("end_time - start_time: {}".format(end_time - start_time))

100个请求耗时37秒左右

vllm测试结果

测试结论:并发性能提升(类比):1236/37 = 33.41倍

ChatGLM3-6B

1卡,HF,8192最大长度

chatglm3-6b接口调用方式与chatglm2-6b保持一致

1卡,vllm,8192最大长度

vllm部署方式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from datetime import datetime
from vllm import LLM, SamplingParams

prompts = ["""<|system|>
You are ChatGLM3, a large language model trained by Zhipu.AI. Follow the user's instructions carefully. Respond using markdown.
<|user|>
晚上睡不着应该怎么办
<|assistant|>
"""]
sampling_params = SamplingParams(temperature=0.8, top_p=0.95, max_tokens=512)

llm = LLM(model="/mnt/lustre/data/chatglm3-6b",
tensor_parallel_size=1,
trust_remote_code=True,
dtype="float16",
gpu_memory_utilization=0.4)
output_ = llm.generate(prompts, sampling_params)

prompts = prompts * 500
start_time = datetime.now()
outputs = llm.generate(prompts, sampling_params)

# Print the outputs.
for output in outputs:
prompt = output.prompt
generated_text = output.outputs[0].text
# print(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")
print(f"Generated text: {generated_text!r}")
end_time = datetime.now()
print("end_time - start_time: {}".format(end_time - start_time))

100个请求耗时39秒左右

本文结束 感谢您的阅读