vllm 随着并发增长,达到第一个令牌的临界慢度 - 在公平性和吞吐量之间取得平衡?

vx6bjr1n  于 2个月前  发布在  其他
关注(0)|答案(8)|浏览(37)

使用OpenAI客户端对vLLM进行pytest并行攻击。在A100上运行这些模型,70b和cabybara ares在4A100 80GB上,Mixtral在2A100 80GB上。
例如,对于70b,我们运行:

python -m vllm.entrypoints.openai.api_server \
        --port=5000 \
        --host=0.0.0.0 \
        --model=h2oai/h2ogpt-4096-llama2-70b-chat \
        --tokenizer=hf-internal-testing/llama-tokenizer \
        --tensor-parallel-size=4 \
        --seed 1234 \
        --trust-remote-code \
	--max-num-batched-tokens 8192 \
        --download-dir=/workspace/.cache/huggingface/hub

或者对于Mistral 7b v0.2:

python  -m vllm.entrypoints.openai.api_server \
        --port=5004 \
        --host=0.0.0.0 \
        --model=mistralai/Mistral-7B-Instruct-v0.2 \
        --tensor-parallel-size=1 \
        --seed 1234 \
        --trust-remote-code \
	--max-num-batched-tokens 131072 \
        --download-dir=/workspace/.cache/huggingface/hub

也就是说,高批量。
或者对于Mixtral:

python -m vllm.entrypoints.openai.api_server \
--port=5002 \
--host=0.0.0.0 \
--model mistralai/Mixtral-8x7B-Instruct-v0.1 \
--seed 1234 \
--tensor-parallel-size=2 \
--max-num-batched-tokens=163840 \
--max-log-len=100

同样具有很高的批量大小。
然而,尽管并发增加导致每秒的令牌数降低是可以理解的,但最令人担忧的是首次获得令牌所需的时间以及有多少请求“不幸”地需要花费甚至长达250秒才能获得第一个令牌。
能否修改vLLM以便我们可以在吞吐量与公平性之间取得平衡?一般来说,我认为在高并发情况下,公平性比总吞吐量更为重要。

以下是一些结果。代码不太好看,但我可以在请求时分享。
vllmstress2_0_1000_0_4096.csv.final.clean.csv
代码丑陋且提示并不完全适用于该模型,但您可以得到大致的想法。我已删除实际IP地址:
stress_vllm_github.py.zip

j9per5c4

j9per5c41#

这是一个关于$31744$个令牌进入Mixtral的另一个例子。对于一些"用户"来说,第一个令牌到达的时间相当糟糕。

$x_1^{c_0}d_1^x$

rkttyhzu

rkttyhzu3#

你好,@pseudotensor。

我还没有检查你的代码,但想先添加一些小建议。

  • 最好使用现有的基准脚本 https://github.com/vllm-project/vllm/tree/main/benchmarks,如果需要的话进行修改,这样会方便其他贡献者。
  • 提高并发率(RPS)不应该降低每秒令牌数,如果是这样,我认为这是一个关键问题。你发布的内容可能只是增加了等待请求的队列。
  • 从调度器的视角来看,不启动新的提示并完成正在运行的提示(这会增加 Fist Token[s])是最大化吞吐量的理想策略。
  • 我同意,能够在不影响吞吐量的情况下减少第一个令牌延迟是更好的选择。

我得到了以下结果:

python -m vllm.entrypoints.api_server --model h2oai/h2ogpt-4096-llama2-13b-chat --swap-space 32  --disable-log-requests -tp 2 --scheduler-policy reorder --scheduler-reorder-window 0.1
python benchmarks/benchmark_serving.py --dataset ShareGPT_V3_unfiltered_cleaned_split.json --backend vllm --model h2oai/h2ogpt-4096-llama2-13b-chat  --request-rate 12
--request-rate -->20010050251212 #2357 reorder-window=0.13
Request throughput(requests/s):2.32.412.382.392.402.692.28
Input token throughput(tokens/s):591598592593596668565
Output token throughput(tokens/s):576582576577580651550
Mean TTFT(ms):17880417421817222716060813795111748926756
Median TTFT(ms):17625517143217084815836413608911826621412
P99 TTFT(ms):38207837219236625134601529958125632773796
Mean TPOT(ms):467745314521420436243085793
Median TPOT(ms):1020992985897754656201
P99 TPOT(ms):3787537806365103432130423250286860
js5cn81o

js5cn81o4#

你能帮我理解这些结果吗?它们与我分享的内容有什么比较?对于12或200个"请求速率",你得到的大约最坏的情况是TTFT在256秒到382秒之间,这真的很糟糕,对吧?
P99可能不是最好的度量标准,因为如果它主要是顺序回填,那么它根本不是一个统计问题,而是一个固定的问题,后面的请求会滞后。

vbkedwbf

vbkedwbf5#

@pseudotensor 添加了 request-rate=3,这是可以处理我的2个 RTX 3090 的最大值。

  • 基准测试发送了1000个请求,而--request-rate仅控制我们发送负载的速度有多快。
  • request-rate增加时,性能没有降低(通过请求/秒、令牌/秒衡量)。vLLM不会堵塞,可以在某个时刻处理所有请求。
  • 因此,所有待处理的请求都在等待轮到它们。这表明随着TTFT的增加(--request-rate gets increased. If we send 1k requests almost instantly ( --request-rate=200`),当以2.3 req/sec的速度处理它时,总时间为约434秒(与TTFT相同顺序)。
  • P99通常是一个很好的指标,但在这里我们从数据文件中混合了长和短提示。也许我们应该添加一个额外的过滤器或按长度进行归一化。
  • 总之:如果一个vLLM节点以2.5 req/s的速度处理请求,我们应该保持这种咀嚼速度在整个可能的工作场景中保持一致。并生成额外的节点来跟上传入的负载。

我注意到有时GPU负载不足。

$ nvidia-smi 
Thu Mar  7 01:09:53 2024       
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 530.30.02              Driver Version: 530.30.02    CUDA Version: 12.1     |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                  Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf            Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  NVIDIA GeForce RTX 3090         On | 00000000:21:00.0 Off |                  N/A |
|100%   84C    P2              278W / 350W|  23929MiB / 24576MiB |     79%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+
|   1  NVIDIA GeForce RTX 3090         On | 00000000:4B:00.0 Off |                  N/A |
|100%   83C    P2              298W / 350W|  23021MiB / 24576MiB |     78%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+
                                                                                         
+---------------------------------------------------------------------------------------+
| Processes:                                                                            |
|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |
|        ID   ID                                                             Usage      |
|=======================================================================================|
|    0   N/A  N/A    119177      C   python                                    23846MiB |
|    1   N/A  N/A    122902      C   ray::RayWorkerVllm.execute_method         22938MiB |
+---------------------------------------------------------------------------------------+

这令人担忧。
我同意,从最终用户的Angular 来看,TTFT是一个非常重要的指标,尤其是当我们将要处理超长的提示时。

von4xj4u

von4xj4u6#

如何使用vllm计算每秒的第一个标记和生成标记的时间?

mwngjboj

mwngjboj7#

如果我的请求速率是x,这意味着我一次发送了x个请求。LML服务器是否会接收到一批x个请求?
如果不是这样,那么我如何以异步方式发送一批请求?

pgpifvop

pgpifvop8#

@rbgo404 这个 --scheduler-delay-factor 功能对于确保更多请求作为一批处理非常有用,通过在调度中添加一个小的延迟。我不确定这是否对收到的第一个请求有效,因为延迟与之前请求的延迟成正比,但即使它在第一个请求上无效,对于一个长时间运行的服务器来说,可以摊销掉,不会有影响。

相关问题