vllm代码分析
总览
vllm官方设计文档
https://github.com/vllm-project/vllm/blob/main/docs/design/
框架整体调度流程
用户代码
│
│ 1. 同步/异步调用
▼
┌──────────────────────────┐
│ LLM / AsyncLLMEngine │◀─ yield / return ───┐
│ ·tokenize&pre-process │ │
│ ·生成 EngineCoreRequest │ │
└┬─────────────────────────┘ │
│ 2. 序列化 + ZMQ PUSH │
│ (ipc:///tmp/xxx_input) │
▼ ▼ │
┌──────────────────────────┐ │
│ EngineCoreClient │ │
│ (用户进程内 RPC 代理) │ │
└┬─────────────────────────┘ │
│ 3. 广播 RPC "add_request" │
│ 给所有 rank │
▼ ▼ │
┌──────────────────────────┐ │
│ EngineCore 进程 (rank0) │ │
│ ·Router 收消息 │ │
│ ·Scheduler 连续批处理 │ │
└┬─────────────────────────┘ │
│ 4. 每步调度产生 SchedulerOutput │
▼ ▼ │
┌──────────────────────────┐ │
│ EngineCore rpc_server │ │
│ ·collective_rpc │ │
└┬─────────────────────────┘ │
│ 5. 再次 ZMQ DEALER 广播 │
│ "execute_model" │
│ 参数:SchedulerOutput │
▼ ▼ ┌──────────────────────────┐ │
┌────────────────┼─▶│ Worker-0 / GPU-0 进程 │ │
│ │ ·recv RPC │ │
│ │ ·ModelRunner.execute │ │
│ │ ·ViT + Transformer │ │
│ │ ·sample + logits │ │
│ │ ·send result back │ │
│ └──────────────────────────┘ │
│ 6. 各 Worker 并行执行 │
│ TP 组内 NCCL AllReduce │
├──────────────────────────┐ │
│ EngineCore (聚合输出) │◀── ZMQ 收结果────────┘
│ ·构造 RequestOutput │
└┬─────────────────────────┘
│ 7. 通过 output_socket PUSH
▼ ▼
┌──────────────────────────┐
│ EngineCoreClient │
│ ·recv_multipart │
│ ·反序列化 │
└┬─────────────────────────┘
│ 8. 塞进 asyncio Queue / threading Queue
▼ ▼
用户代码 ◀─ yield RequestOutput ◀─ Queue.get()
初始化
AsyncLLM
self.engine_core = EngineCoreClient.make_async_mp_client()
EngineCoreClient
AsyncMPClient
1)调用MPClient的初始化,创建各级数据结构;
2)启动输出队列监听;
asyncio.get_running_loop()
self._ensure_output_queue_task()
MPClient
with launch_core_engines(
vllm_config,
executor_class,
log_stats,
addresses,
) as (engine_manager, coordinator, addresses):
self.resources.coordinator = coordinator
self.resources.engine_manager = engine_manager
-> launch_core_engines -> CoreEngineProcManager -> EngineCoreProc.run_engine_core
CoreEngineProcManager会使用多进程的方式运行 EngineCoreProc.run_engine_core
EngineCoreProc
run_engine_core
-
初始化并创建engine_core
-
启动engine_core.run_busy_loop
run_busy_loop
循环执行以下两个任务:
1)self._process_input_queue从请求队列上接受请求;如果是新请求(ADD),将请求添加到Scheduler中。
2)self._process_engine_step处理请求,并返回结果。self.step_fn(self.step_with_batch_queue)执行返回的结果通过self.output_queue.put_nowait(output)挂载返回。
EngineCore
1)创建self.model_executor;
2)创建self.scheduler,为Scheduler绑定kv_connector;
3)确认ec_producer/consumer身份。
Executor
使用多进程方式拉起多个worker。
-> WorkerProc.make_worker_process
Worker
一个Worker管理一张显卡,并调用ModelRunner执行任务。
ModelRunner
模型的实际持有者,管理模型的推理,如execute_model, sample_tokens。
请求调用链
AsyncLLM.generate
非流式输入
-> AsyncLLM.add_request -> AsyncLLM._add_request -> self.engine_core.add_request_async(request)
流式输入
vllm是支持流式输入的。
EngineCoreClient
-> AsyncMPClient.add_request_async
-> AsyncMPClient._send_input(EngineCoreRequestType.ADD, request)
-> AsyncMPClient._send_input_message
_send_input_message通过self.input_socket.send_multipart向多个EngineCoreProc队列中发送请求message。
EngineCoreProc
(zmq socket)---> ---> --->
调用engine_core.run_busy_loop。EngineCore管理ModelExecutor和Scheduler。
run_busy_loop
循环执行以下两个任务:
1)self._process_input_queue从请求队列上接受请求;如果是新请求(ADD),将请求添加到Scheduler中。
2)self._process_engine_step处理请求,并返回结果。self.step_fn(self.step_with_batch_queue)执行返回的结果通过self.output_queue.put_nowait(output)挂载返回。
step_with_batch_queue
执行一个step的调度、执行,并返回结果。
self.scheduler.schedule()
self.model_executor.execute_model()
self.model_executor.sample_tokens()
engine_core_outputs = self.scheduler.update_from_output()
Executor
使用zmq建立连接,并在未来使用broadcast语义将请求发送给所有worker。
定义rpc调用,调用worker的同名方法执行execute_model、sample_tokens。
Worker
->self.workers=WorkerProc->WorkerWrapperBase->gpu_worker.Worker
Worker(gpu_worker.py)
->model_runner=vllm.v1.worker.gpu_model_runner.GPUModelRunner
ModelRunner
GPUModelRunner
GPUGenerationModelRunner
模型计算
包含GPUModelRunner.execute_model和GPUModelRunner.sample_tokens。
1)execute_model负责接受输出,主模型和logits模型计算,输出得到logits和hidden_states。
2)sample_tokens从logits采样获得输出token。
execute_model
prefill阶段:
inputs -> model_output: [6, 2560] -> (只取最后一个进行计算)sample_hidden_states:[1,2560] -> logits:[1, 151936]
创建ExecuteModelState保存hidden_states/logits等,并存放在数据结构self.execute_model_state中。
sample_tokens
采样并打包成ModelRunnerOutput返回,异步情况下使用AsyncGPUModelRunnerOutput返回。
结果返回
GPUModelRunner
一般情况下由GPUModelRunner.sample_tokens返回本轮推理的输出,核心信息为sampled_token_ids。
GPUModelRunner.sample_tokens -> Worker.sample_tokens -> (collective_rpc)Executor.sample_tokens -> EngineCore.step_with_batch_queue
EngineCore
通过future.result异步接收(多进程创建的)worker返回的输出output。
与scheduler_output一起,组成engine_core_outputs返回。
EngineCore.step_with_batch_queue -> EngineCore._process_engine_step -> EngineCore.output_queue.put_nowait(output) --->(zmq)---> EngineCoreClient
EngineCoreClient
初始化(创建)阶段就会开启输出队列的监听。
从self.resources.output_socket中接收输出EngineCoreOutputs,并使用outputs_queue.put_nowait将EngineCoreOutputs至self.outputs_queue。
AsyncLLM
q=await self.add_request(); out = await q.get();
-> self.add_request: await self._add_request(..., queue)
->
推理框架理解Q&A
1.vllm使用的套接字socket是什么套接字,vllm目前只管理本机的进程/GPU,该套接字是最好的选择吗?
zmq套接字。是高级语言的进/线程通信工具。
2.EngineCoreClient、EngineCoreProc、EngineCore分别的作用?
EngineCoreClient使用CoreEngineProcManager初始化并管理EngineCoreProc。CoreEngineProcManager会使用multiprocessing创建多个EngineCoreProc,EngineCoreProc的数量由local_engine_count指定。EngineCoreProc继承EngineCore类,主要增加了与EngineCoreClient侧的通信。
3.ModelExecutor、worker、model_runner各自的职责和定义?ModelExecutor对外提供统一的调用返回接口,屏蔽不同卡、单卡/多卡分布式细节;一个Worker管理一张卡;ModelRunner管理模型。
4.异步调度下,为什么EngineCore接收到的输出比GPUModelRunner返回的输出多?GPUModelRunner可能会在结果未返回前进行下一次调度处理。
多模态
数据结构
MultiModalDataItems
每个请求的所有多模态数据都由这个类管理,访问方式为mm_data_items[modality][idx], idx为同一模态多个数据准备(如qwen3vl可以喂入多张图片)
MultiModalFeatureSpec
请求req中的多模态数据
请求生成与发送
llm.generate:接收请求,并将请求挂在到request队列上,从输出队列上取结果,进行后处理并返回
->llm._add_request->llm_engine.add_request
1 input_processor.process_inputs: 将字符转换成token,将图片切割patch并转换为visual token。
2 output_processor.add_request
3 engine_core.add_request->SyncMPClient.add_request->SyncMPClient._send_input(EngineCoreRequestType.ADD, request)
->llm.llm_engine.step: 进行一轮推理,返回该轮推理的输出 ->llm_engine.engine_core: engine_core.get_output,从输出队列上取结果
1 请求预处理
完成:1)将字符转换成token,将图片切割patch并转换为visual token。2)将visual token缓存至cache,如果cache有该图片的缓存,则从缓存中读取visual token而跳过图片预处理步骤。
LLM.input_processor.process_inputs->InputProcessor.process_inputs->InputPreprocessor.preprocess
1.1 InputPreProcessor._prompt_to_llm_inputs
->......->mm_processor.apply
BaseMultiModalProcessor.apply
输入:
Prompt: str, 原始问句;
mm_data: dict, 原始mm data, 如图片存在mm_data['image']
输出:
MultiModalInputs, 成员变量如下
prompt_token_ids
mm_kwargs
mm_hashes
mm_placeholders
- self._cached_apply_hf_processor
字符和图片转token,并标注占位符等其他数据。
a.将提示文本与多模态数据一并送入 HF Processor,一次性得到 token ID 序列和预处理后的图片张量。
->Qwen3VLMultiModalProcessor.apply->Qwen3VLMultiModalProcessor._cached_apply_hf_processor
->Qwen3VLMultiModalProcessor._apply_hf_processor
Qwen3VLMultiModalProcessor._apply_hf_processor_main
_apply_hf_processor_text_only:将prompt(包含特殊符号<|im_start|>等)转换成prompt_id。
_apply_hf_processor_mm_only->_apply_hf_processor_text_mm->_call_hf_processor[=Qwen3VLProcessor]: 将图片切分成固定尺寸(模型要求)的patch,并返回pixel_values: size=[patch_h_num*patch_w_num, hid_dim]、image_grid_thw: [[ 1, patch_h_num, patch_w_num]]。
b. 在 token ID 序列中定位并替换“占位 token”,占位 token 的数量 = 多模态编码器输出特征的条数。例如占位符长度为972,那么原prompt中会插入972个占位token。
c. 从处理后的 token ID 序列里抽取出这些占位 token 的信息。信息记录在mm_position变量中。
- self._maybe_apply_prompt_updates
将图片数据插入prompt_ids,并记录占位符信息。
1.2 其他
InputProcessor和InputPreprocessor剩余部分未对数据本身进行转换,只是定义了新的class去包裹,最终在InputProcessor.process_inputs中放在EngineCoreRequest中传递给receive侧。
2 请求发送
engine_core.add_request->SyncMPClient.add_request->SyncMPClient._send_input(EngineCoreRequestType.ADD, request)
SyncMPClient.resources管理了engine_manager(CoreEngineProcManager),以及llm_engine与core_engine通信的套接字socket。
SyncMPClient.resources.engine_manager.processes后台启动了多个engine_core,并记录了其句柄。
3 请求发送数据结构
->MultiModalProcessingInfo + prompt_ids,
kwargs
hashes
prompt_updates
->MultiModalInputs:
prompt_token_ids: 已经插入了多模态占位符的tokens, list长度为[972+prompt_len]。
mm_kwargs: Dict结构的mm data,image的data为一个list,list中每一个元素为一张图片的信息,包含{pixel_value:MultiModalFieldElem存储image token的tensor, tensor形状为[3888, 1536]; image_grid_thw: MultiModalFieldElem指示image token第一维度形状的长宽,tensor=[1, 72, 54]}
mm_hashes:
mm_placeholders: {'image': [PlaceholderRange(offset=15, length=972, is_embed=None)]}
MultiModalInputs两部分会在EngineCoreRequest(类型MultiModalFeatureSpec)中分别用data和identifier传入EngineCore计算。
->EngineCoreRequest
request_id
prompt_token_ids
mm_features: list[MultiModalFeatureSpec], 其中多模态数据仍然以MultiModalFeatureSpec数据结构传输(该结构为mm data+mm hash)构成。
sampling_params
请求接收与执行
1 请求接收
MPClient利用zmq socket将request传输给(多个)EngineCoreProc
SyncMPClient._send_input ==> EngineCoreProc.process_input_sockets
EngineCoreProc.process_input_sockets:这里处理解析socket传来的数据,还会对ADD类型的数据,即模型推理数据进行缓存的读取。个人觉得把缓存数据读取放在这里不太好。
1)接收数据并分析请求类型
type_frame, *data_frames = input_socket.recv_multipart(copy=False)
request_type = EngineCoreRequestType(bytes(type_frame.buffer))
2)如果请求类型为ADD,对请求做preprocess处理
request = add_request_decoder.decode(data_frames)
request = self.preprocess_add_request(request)
- 将请求放入input_queue, 等待EngineCore.run_busy_loop从队列中读取数据
self.input_queue.put_nowait((request_type, request))
2 请求调度:DPEngineCoreProc
┌─────────────────────────────────────────────────────────────────────────────┐
│ Batch Queue 调度流程(三阶段流水线) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Batch Queue 结构: │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Batch 0 │ → │ Batch 1 │ → │ Batch 2 │ (FIFO 队列,最大容量=3) │
│ │ (GPU上) │ │ (GPU上) │ │ (GPU上) │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ 调度循环 step_with_batch_queue(): │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 阶段1: 尝试填充 Batch Queue │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ if batch_queue 未满: │ │ │
│ │ │ scheduler_output = scheduler.schedule() ← 调度新请求 │ │ │
│ │ │ exec_model_fut = _execute_model(scheduler_output) │ │ │
│ │ │ batch_queue.put(exec_model_fut) ← 放入队列,不等待完成 │ │ │
│ │ │ return EngineCoreOutputs(outputs=[], ...) ← 立即返回空结果 │ │ │
│ │ │ # 优先级:填充队列 > 获取输出 │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 阶段2: 等待队列中最早的 Batch 完成 │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ else: # batch_queue 已满或无法继续调度 │ │ │
│ │ │ exec_model_fut = batch_queue.get() ← 取出最早的 batch │ │ │
│ │ │ exec_model_fut.result() ← 阻塞等待 GPU 执行完成 │ │ │
│ │ │ # 此时 execute_model 已完成,hidden_states 在 GPU 上 │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 阶段3: 采样并更新调度器 │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ model_output = _sample_tokens(scheduler_output) │ │ │
│ │ │ # 从 execute_model_state 取出 hidden_states,执行采样 │ │ │
│ │ │ return scheduler.update_from_output(scheduler_output, model_output) │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
数据结构:DPEngineCoreProc->
调度链:
DPEngineCoreProc._handle_client_request
->DPEngineCoreProc.run_busy_loop
EngineCoreProc会等待请求队列的请求,并根据请求类型处理(_handle_client_request),其中模型推理请求类型为‘ADD’。
EngineCoreProc.run_busy_loop轮询请求,并每轮执行:
1)_process_input_queue->_handle_client_request->add_request
EngineCoreProc.add_request->Scheduler.add_request
2)_process_engine_step->step_fn
self.step_fn()->step
self.output_queue.put_nowait(output)
->DPEngineCoreProc.step-->EngineCore.step
self.scheduler.schedule
self.model_executor.execute_model
EngineCoreProc请求的下发与结果的采集:异步调用self.model_executor.execute_model与self.model_executor.sample_tokens,使用future句柄采集结果。
3 Executor与GPUWorker
Executor使用rpc调度的方式调用workers进行gpu的推理计算,workers由WorkerWrapperBase管理
self.model_executor.execute_model->Executor.execute(MultiprocExecutor)-> MultiprocExecutor.collective_rpc('execute_model')->WorkerWrapperBase.execute_model
GPUWorker.execute_model-> ModelRunner -> GPUModelRunner.execute_model
GPUModelRunner的初始化中,提前申请了很多GPU的tensor,为GPU graph(compile作准备)。
GPUModelRunner.execute_model
4 模型推理:GPUModelRunner
由self.execute_model和self.sample_tokens组成。前者执行模型推理得到logits,后者进行采样得到token。两者由self.execute_model_state串联,保证执行顺序。
GPUModelRunner.execute_model
- self._execute_mm_encoder
2)self._model_forward
2.1)llm模型推理
输入:input_ids,positions;
输出:model_output, tensor[batch, hidden_dim]
2.2)vlm模型推理
输入: inputs_embeds[token_nums, hidden_dim(=2048)], positions[3, token_nums]
输出:model_output, tensor[batch, hidden_dim(=2048)]
5 接收执行数据结构
->EngineCoreRequest
(从cache中取出缓存数据构成完整的request)
->SchedulerOutput
scheduled_new_reqs: NewRequestData{prompt_token_ids, mm_features, }
mm 缓存复用
InputProcessor
设计理念:针对每个模型和配置参数,生成推理请求各模态的每个数据item的hash和cache;同一模态的同一uuids代表着完全相同的模态数据,如图片,给同一uuids再次传入新的图片data将被忽略。vllm会在req process阶段计算hash和需要增加(missing)的模态数据,并传给worker,worker侧会管理hash与对应的模态数据。
一、创建
InputProcessor创建cache:
self.mm_processor_cache = processor_cache_from_config(vllm_config, mm_registry)
->MultiModalProcessorSenderCache(model_config): self._cache = MultiModalCache.get_lru_cache
->MultiModalCache.get_lru_cache->LRUCache
二、处理流程
->InputProcessor.process_inputs->InputPreProcessor.preprocess
mm_cache由InputProcessor管理, 数据结构InputProcessor.mm_processor_cache,数据结构MultiModalProcessorSenderCache
->InputPreProcessor._preprocess->InputPreProcessor._process_decoder_only_prompt->InputPreProcessor._prompt_to_llm_inputs->InputPreProcessor._process_text->InputPreProcessor._process_multimodal
->Qwen3VLMultiModalProcessor.apply->BaseMultiModalProcessor.apply
BaseMultiModalProcessor.cache = MultiModalProcessorSenderCache
1. BaseMultiModalProcessor._cached_apply_hf_processor:处理mm_items缓存,如果未进行缓存,则新增hash和缓存,如果有缓存,则检查是否需要更新缓存;最后返回req推理需要的prompt_ids, mm_info
1)_hash_mm_items: 计算本次请求全部mm items的hash值
使用model_id, modality, item, hf_processor_mm_kwargs, tokenization_kwargs为每个模态的每个item生成mm_hashes值,并用字典保存。其中后四项同一模态(如qwen3vl是it->t)维持不变,注意此处计算hash值只与item的类型挂钩,与item.data的实际值无关。hf_processor_mm_kwargs, tokenization_kwargs打印出来为空,暂时未用。
2)_get_cache_missing_items: 根据hash值验证哪些items已经被缓存,并返回未被缓存的items
返回值:
mm_is_cached:标注各模态是否存在cache数据;
mm_missing_data_items:还没有被缓存的cache数据;
3)_apply_hf_processor_main:对未进行缓存的data items进行preprocess,转换成模型推理需要的形式
输入:
prompt:str,输入的句子。
mm_missing_data_items: MultiModalDataItems,所有未缓存的多模态数据
输出:
prompt_id: prompt转换处理的token,还未进行mm插值。
mm_missing_processed_data:Dict[str, List[Dict[str,tensor]]], 转换后(以及转换成多模态模型推理需要格式)的mm数据。
is_update_applied:一直是False,暂时没用。
4)->mm_missing_kwargs:每个模态的数据是一个list,list的每个元素为推理需要的tensor。
- _merge_mm_kwargs
2. BaseMultiModalProcessor._maybe_apply_prompt_updates
prompt_ids: List[int[, 填充了占位符的tokens。
mm_placeholders: 指示占位符开始的位置,以及占位符本身。
EngineCoreProc
cache由EngineCoreProc管理: EngineCore.mm_receiver_cache
EngineCoreProc.process_input_sockets->EngineCore.preprocess_add_request
->EngineCore.mm_receiver_cache.get_and_update_features->BaseMultiModalReceiverCache.get_and_update_features
->BaseMultiModalReceiverCache.get_and_update_item
feature.data = self.get_and_update_item(feature.data, feature.identifier)
self._cache[mm_hash] = mm_item
Note: WorkerWrapperBase处也写了mm_cache的代码和逻辑,但是v0.13.0更新和读取cache都在EngineCoreProc。
online
启动
vllm/entrypoints/cli/main.py->vllm/entrypoints/cli/serve.py:
->ServeSubcommand.cmd->uvloop.run(run_server(args))->vllm/entrypoints/openai/api_server.py:run_server
->run_server_worker
->build_async_engine_client->build_async_engine_client_from_engine_args
->async_llm = AsyncLLM.from_vllm_config->AsyncLLM.__init__
1.InputProcessor/OutputProcessor与LLM相同
2.engine_core:后台拉起的EngineCoreProc和LLM复用MPClient代码
EngineCoreClient.make_async_mp_client->AsyncMPClient
请求传递与执行
https请求在哪里转换成async_engine需要的req的,req的内容是什么样子的?
1. 从路由接收请求
->api_server.py:: create_chat_completion, 接受的ChatCompletionRequest,即承载着原始http请求字段;
2. 将OPENAI格式的request转换成LLMEngine格式的request。
->OpenAIServingChat.create_chat_completion
- self._preprocess_chatrequest
输出:conversation, engine_prompts
conversation:[{'role': 'user', 'content': [{'type': 'text', 'text': '图片里面有什么?'}, {'type': 'image'}]}]
engine_prompts:[{'prompt_token_ids': [151644, 872, 198, 45930, 100420, 104139, 30, 151652, 151655, 151653, 151645, 198, 151644, 77091, 198], 'prompt': '<|im_start|>user\n图片里面有什么?<|vision_start|><|image_pad|><|vision_end|><|im_end|>\n<|im_start|>assistant\n', 'multi_modal_data': {'image': [MediaWithBytes(media=<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=1920x2530 at 0x7F0828425430>, original_bytes=b'\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00C\x00\x08\x06\x06\x07\x06\x05\x08\x07\...)]}, 'multi_modal_uuids': {'image': [None]}}]
2)self._process_inputs
输出:engine_request:EngineCoreRequest
3)self.engine_client.generate
这里调用到AsyncLLM.generate
输出:AsyncGenerator[RequestOutput, None]
调度图总结
HTTP POST /v1/chat/completions
│
▼
create_chat_completion()
│
├─> chat_completion_parser() ──> prompt: str
├─> request_id = f"cmpl-xxx"
├─> sampling_params = request.to_sampling_params()
▼
engine.generate(prompt, sampling_params, request_id)
│
▼
AsyncLLMEngine.generate()
│
├─> prompt_token_ids = tokenizer.encode(prompt)
├─> seq = Sequence(...)
├─> seq_group = SequenceGroup(seqs=[seq], ...)
▼
scheduler.add_seq_group(seq_group) # ← 进入调度队列
metrics
offline
指标
Request重要时间点:
queued_ts: engine拿到request的时间戳
scheduled_ts: engine调度到request并开始执行的时间戳
first_token_ts: engine输出第一个token的时间戳
last_token_ts: engine输出最后一个token的时间戳
根据上述时间戳,统计时延latency:
scheduled_latency: 调度时延,scheduled_ts-queued_ts
prefill_latency: prefill阶段时延,first_token_ts-scheduled_ts
decode_latency: decode阶段输出一个token的平均时延,(last_token_ts-first_token_ts)/(num_generation_tokens-1)
Note
1)offline的请求preprocess是同步阻塞的,所以在请求多时统计端到端时延没有意义,因为会累积preprocess的时延;
online
latency
vllm:time_to_first_token_seconds:首 Token 延迟 (TTFT)
vllm:request_time_per_output_token_seconds:每输出 Token 耗时 (TPOT)
vllm:e2e_request_latency_seconds:端到端请求延迟
vllm:request_queue_time_seconds:请求排队时间
vllm:request_inference_time_seconds:推理时间
vllm:request_prefill_time_seconds:Prefill 阶段耗时
vllm:request_decode_time_seconds:Decode 阶段耗时
vllm:request_generation_tokens:生成的总token数量
kv cache
metrics查看的kv cache使用百分比仅针对正在运行的请求,已经结束的req的cache block会被挂载到free队列
浙公网安备 33010602011771号