背景介紹
- 硬件
A800 80G - 模型
chat-glm4-9b-128K - 環境
生產 - 正常顯存占用情況
glm4 占用32GB
其他顯存工占用38GB左右
總共剩余10GB。
問題描述
推理時報錯日志,由于內網環境無法拿出日志,與下面的類似。
File "/data/miniconda3_new/envs/vllm-new/lib/python3.10/site-packages/vllm/engine/async_llm_engine.py", line 654, in add_requestself.start_background_loop()File "/data/miniconda3_new/envs/vllm-new/lib/python3.10/site-packages/vllm/engine/async_llm_engine.py", line 476, in start_background_loopraise AsyncEngineDeadError(
vllm.engine.async_llm_engine.AsyncEngineDeadError: Background loop has errored already.
再往前追述日志,發現有超長文本請求,字符長度10萬左右,顯存不夠報類似如下錯誤
torch.cuda.OutOfMemoryError: CUDA out of memory. Tried to allocate 10.78 GiB
問題分析
根本原因還是顯存不夠,但是一個請求推理顯存不夠后這個請求失敗應該釋放調占用的顯存,不應該影響之后的請求才對。
引擎啟動代碼如下:
if __name__ == "__main__":MODEL_PATH = sys.argv[1]tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH, trust_remote_code=True)engine_args = AsyncEngineArgs(model=MODEL_PATH,tokenizer=MODEL_PATH,# 如果你有多張顯卡,可以在這里設置成你的顯卡數量tensor_parallel_size=1,dtype="bfloat16",#dtype="half",trust_remote_code=True,# 占用顯存的比例,請根據你的顯卡顯存大小設置合適的值,例如,如果你的顯卡有80G,您只想使用24G,請按照24/80=0.3設置gpu_memory_utilization=0.4,enforce_eager=True,worker_use_ray=False,disable_log_requests=True,max_model_len=MAX_MODEL_LENGTH, # 這里是128000)engine = AsyncLLMEngine.from_engine_args(engine_args)uvicorn.run(app, host='0.0.0.0', port=8000, workers=1)
引擎調用部分代碼如下:
sampling_params = SamplingParams(**params_dict)
try:async for output in engine.generate(inputs=inputs, sampling_params=sampling_params, request_id=f"{time.time()}"):output_len = len(output.outputs[0].token_ids)input_len = len(output.prompt_token_ids)ret = {"text": output.outputs[0].text,"usage": {"prompt_tokens": input_len,"completion_tokens": output_len,"total_tokens": output_len + input_len},"finish_reason": output.outputs[0].finish_reason,}yield ret
except Exception as e:logger.error(f"錯誤:{e}")raise e
finally:gc.collect()torch.cuda.empty_cache()
- 引擎崩潰后每次也是這里的logger.error輸出的Background loop has errored already.
- 第一次內存不夠報錯日志也是這里有顯示,并且有調用finally模塊中的清空緩存邏輯。
- 還是重復上面的問題,一個請求不應該導致整個引擎崩潰,從而導致之后的請求也無法處理,不知道是否是vllm的bug??? 看到能多人也在github上提了issue,但是目前無解決方案。參考:
https://github.com/vllm-project/vllm/issues/6361
解決方案
經過上面的分析我們知道是顯存不夠引起的,我們這里只對顯存不夠做調優來減少這種情況發生,并不能解決顯存不夠后引發的引擎崩潰問題。
做如下參數調整
- enable-chunked-prefill = True
- max_num_batched_tokens = 8192
- max_num_seqs = 10
參數介紹
enable-chunked-prefill
- 是 vLLM 中的一個優化功能,主要用于處理長上下文或大提示(prompt)情況下的內存和計算效率問題。默認False
max_num_batched_tokens
預填充階段的最大批處理token數:
-
當 enable-chunked-prefill=True 時,決定每個chunk(塊)的最大token數
-
解碼階段的最大批處理token數:限制解碼階段同時處理的token總數
-
工作原理
當啟用 chunked prefill 時:
系統將長prompt分割為多個chunk
每個chunk的token數不超過 max_num_batched_tokens
依次處理這些chunk
例如:
如果prompt有5000 tokens,max_num_batched_tokens=2048
將被分割為:2048 + 2048 + 904 tokens的三個chunk -
默認值
不同的顯卡,不同的內存默認值不一樣
詳細邏輯見官方代碼:https://github.com/vllm-project/vllm/blob/main/vllm/engine/arg_utils.py -
建議
高端GPU(如A100 80GB):可設較大值(4096-8192)
消費級GPU(如3090 24GB):建議較小值(1024-2048)
max_num_seqs
控制并發數量,如果你的顯存不夠是由于并發引起的,可以設置這個參數。
其他參數:
- max_model_len : 允許模型處理的最大token數量,可根據實際情況限制,由于我們就是要處理長文本的,所以我這里沒有調整。
- gpu_memory_utilization GPU顯存占比,盡量初始化時小一點,預留足夠的顯存空間。
總結
經測試以上參數調整后可以顯著控制GPU的占用情況,減少OutOfMemory情況的發生,提高系統可用性,后續也會嘗試升級VLLM版來解決崩潰后無法處理后續請求的問題,但是顯存都是稀缺資源,本身也要做好調優。