vLLM × vllm-ascend · 联动分析 ①全景②线一 算子级③线二 服务级 ④版本演进与坑⑤怎么选⑥链接

昇腾性能数据采集全流程:上游 vLLM 与 vllm-ascend 联动分析

vLLM main bbd7c24(2026-09-24 克隆) vllm-ascend a71b766ce(2026-09-23) 采集工具两条线:Ascend PyTorch Profiler(算子级)· MS Service Profiler(服务级)

① 全景:两条采集线,一套上游骨架

推理服务的性能采集要回答两类问题:"哪个算子慢"(算子/内核级)和 "服务流程哪一段慢"(调度、批处理、KV 管理的函数级)。昇腾官方指南给出两条对应的采集线:

特性线一:Ascend PyTorch Profiler线二:MS Service Profiler
安装内置,命令行即开需从源码构建 msserviceprofiler(CANN Toolkit 预装)
采集粒度PyTorch 算子级服务框架函数级(也可含算子)
控制方式API 请求控制(/start_profile、/stop_profile)配置文件控制(enable 0→1)
数据格式ascend_pt 格式Chrome Tracing + CSV
适用场景模型算子性能分析服务框架工作流分析

两条线共用上游 vLLM 的骨架:ProfilerConfig(参数)、 Worker.profile()(入口)、WorkerProfiler(生命周期状态机)。 vllm-ascend 做两件事:把线一的执行器换成 torch_npu.profiler; 给线二生成昇腾专用的符号打点表。这就是本页的两条主线。

这张图回答什么问题:两条采集线各自的上游骨架与昇腾替换点在哪——全景一张图。
图 1 · 全景:两条采集线与联动点(结构视角)
flowchart TD A["上游 vLLM 骨架
ProfilerConfig · Worker.profile · WorkerProfiler 状态机"] --> B["线一:Ascend PyTorch Profiler(算子级)"] A --> C["线二:MS Service Profiler(服务级)"] B --> B1["vllm-ascend 替换:
TorchNPUProfilerWrapper
(torch_npu.profiler)"] C --> C1["vllm-ascend 供表:
profiling_config.py 生成
service_profiling_symbols.yaml"] B1 --> D1["ascend_pt 数据 → MindStudio Insight"] C1 --> D2["Chrome Tracing + CSV
request/batch/kvcache"] style B1 fill:#eef3ff,stroke:#5b6ee1 style C1 fill:#eef3ff,stroke:#5b6ee1

② 线一:Ascend PyTorch Profiler(算子级)

2.1 使用流程:五步拿数据

启动服务时带 --profiler-config(只设了它,/start_profile 端点才会注册,否则 404); 稳定后 curl 开采集 → 跑业务请求 → curl 停采集 → 离线 analyse → 用 MindStudio Insight 看。 注意 2026-01-19 起上游废弃了 VLLM_TORCH_PROFILER_DIR 环境变量(vllm-ascend PR #5928),统一用 --profiler-config '{"profiler":"torch","torch_profiler_dir":"./vllm_profile","torch_profiler_with_stack":false}'。

这张图回答什么问题:从启动到看到数据,五步怎么走。
图 2 · 线一五步流程(执行视角)
flowchart TD A["① 启动服务
--profiler-config JSON"] --> B["② curl /start_profile
(未配置时端点不存在→404)"] B --> C["③ 发业务请求
(稳定业务数据后采几秒即可)"] C --> D["④ curl /stop_profile
数据自动落盘 torch_profiler_dir"] D --> E["⑤ analyse() 解析
→ ASCEND_PROFILER_OUTPUT → Insight"] style E fill:#dafbe1,stroke:#1a7f37

2.2 联动机制:上游骨架四件套,昇腾只换执行器

上游侧(vLLM main):ProfilerConfig 定义参数; gpu_worker.profile(is_start)(vllm/v1/worker/gpu_worker.py:1297)是入口; WorkerProfiler(vllm/profiler/wrapper.py)是生命周期状态机——管理 delay_iterations(启动后延迟几步再采)、max_iterations(最多采几步), 每步 worker 循环调一次 step() 推进状态; 工厂 create_worker_profiler(wrapper.py:675)按 profiler 类型发 TorchProfilerWrapper / CudaProfilerWrapper / ProtonProfilerWrapper ——只有 CUDA 三分支,没有 NPU。

昇腾侧(vllm-ascend):NPU 的 NPUWorker.profile() (vllm_ascend/worker/worker.py:1170)不走上游工厂,直接实例化 TorchNPUProfilerWrapper(worker/worker.py:1177)—— 它继承上游 WorkerProfiler(状态机、延迟/步数限制全部复用), 只把底下的执行器从 torch.profiler 换成 torch_npu.profiler。 "继承骨架、替换执行器"就是这条线的全部联动设计。

这张图回答什么问题:一次 /start_profile 从 API 到 torch_npu.profiler 要经过谁,上游和 ascend 各管哪段。
图 3 · 联动调用链(执行视角,节点含文件与行号)
sequenceDiagram participant API as /start_profile participant W as NPUWorker
worker.py:1170 participant WP as NPU Wrapper
torch_npu_profiler.py:29 participant UP as WorkerProfiler
wrapper.py:33 API->>W: profile(True) W->>WP: 首次创建并 start(:1177) WP->>UP: 复用基类 start/step/stop WP->>WP: _start → torch_npu.profiler(:79) Note over WP,UP: 每步 step():延迟启动/到量自停 API->>W: profile(False) W->>WP: stop → trace 落盘
代码 A1 · 昇腾执行器:把 torch.profiler 换成 torch_npu.profiler
vllm_ascend/profiler/torch_npu_profiler.py · TorchNPUProfilerWrapper · L29-86class TorchNPUProfilerWrapper(WorkerProfiler):
    """Subclass of vLLM ``WorkerProfiler`` that wires in ``torch_npu.profiler``."""

    @staticmethod
    def _create_profiler(profiler_config, trace_name):
        ...
        msmonitor_use_daemon = get_ascend_config().msmonitor_use_daemon  # ↳昇腾侧配置
        if msmonitor_use_daemon:
            raise RuntimeError("msmonitor_use_daemon and torch profiler 不能同开")  # ⚡互斥红线

        experimental_config = torch_npu.profiler._ExperimentalConfig(
            export_type=torch_npu.profiler.ExportType.Text,      # ↳导出文本型产物
            profiler_level=torch_npu.profiler.ProfilerLevel.Level1,
            aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization,  # ⚡AI Core 各流水线利用率
            data_simplification=True,                            # ↳数据精简,控制体积
        )
        return torch_npu.profiler.profile(
            activities=[CPU, NPU],            # ⚡与 CUDA 版的关键差异:NPU activity
            with_stack=False,
            profile_memory=profiler_config.torch_profiler_with_memory,
            # NOTE: torch_npu 的 with_modules 等价于 torch.profiler.with_stack,
            # with_stack 在 torch_npu 上开销显著 → 用 with_modules 替代(⚡昇腾特有取舍)
            with_modules=profiler_config.torch_profiler_with_stack,
            on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(
                profiler_config.torch_profiler_dir, worker_name=trace_name),
        )

    def _start(self): self.profiler.start()
    def _stop(self): self.profiler.stop()
    def _profiler_step(self): return True
代码 A2 · 昇腾入口:不走上游工厂,直接建 wrapper
vllm_ascend/worker/worker.py · NPUWorker.profile · L1170-1184        if is_start:
            rank_suffix = get_worker_rank_suffix(global_rank=self.rank)
            trace_name = f"{profile_prefix}_{rank_suffix}" if profile_prefix else rank_suffix

            if self.profiler is None:
                # ⚡上游 create_worker_profiler 只有 torch/cuda/proton 三分支(无 NPU),
                #   因此 NPUWorker 在这里直接实例化昇腾 wrapper(联动替换点)
                self.profiler = TorchNPUProfilerWrapper(self.profiler_config, trace_name)
                self.profiler.start()
            else:
                # 已初始化:重启采集但保留首次的 trace 名
                self.profiler.start()

2.3 产物与 PD 分离注意

停采集后 torch_profiler_dir 下出现 *_ascend_pt 目录,先调 torch_npu.profiler.profiler.analyse() 解析,再看 ASCEND_PROFILER_OUTPUT: kernel_details.csv(内核级)、operator_details.csv(算子级)、 op_statistic.csv(算子利用率)、step_trace_time.csv(步调度)、 trace_view.json(Chrome tracing,用 MindStudio Insight 打开)—— 这正是半年目标 KR1.3 里"profile 分解 Cube/Vector/通信"的数据来源。

PD 分离下 P/D 是两个独立实例:两边都要带 --profiler-config(目录错开), 负载均衡代理不转发 /start_profile、/stop_profile——必须分别 curl 各节点; EPD 代理(layerwise)则支持向全部 E/P/D 实例广播。

③ 线二:MS Service Profiler(服务级)

3.1 使用流程:配置驱动,不用 curl

这张图回答什么问题:服务级采集的闭环——两个环境变量、一个开关、一条 parse 命令。
图 4 · 线二流程(执行视角)
flowchart TD A["构建 msserviceprofiler
(CANN 预装/源码升级)"] --> B["服务启动前设两个环境变量
SERVICE_PROF_CONFIG_PATH + PROFILING_SYMBOLS_PATH"] B --> C["sed 把 enable 0→1
(配置文件控制采集开关)"] C --> D["正常发业务请求
数据落 ~/.ms_server_profiler"] D --> E["msserviceprofiler parse
→ chrome_tracing.json + request/batch/kvcache.csv"] style E fill:#dafbe1,stroke:#1a7f37

3.2 符号表机制:vllm-ascend 把"打点清单"生成好

线二的核心配置是一份符号表 YAML:声明"哪些函数要打点、用什么 handler 记录"。 vllm_ascend/profiling_config.py 在 vLLM 启动时自动生成 ~/.config/vllm_ascend/service_profiling_symbols.<版本>.yaml (文件名带 vLLM 版本号,generate_service_profiling_config L538 原子写入)。 清单覆盖服务全链路,且每条可带 min_version/max_version——上游接口一变, 换一条符号声明即可,这是它对齐上游快速演进的关键手段:

代码 A3 · 符号表长什么样:按版本条件的打点声明
vllm_ascend/profiling_config.py · SERVICE_PROFILING_SYMBOLS_YAML · 节选(L27-157)- symbol: vllm.v1.core.sched.scheduler:Scheduler.schedule   # ↳上游调度入口
  min_version: "0.9.1"
  handler: ms_service_profiler.patcher.vllm.handlers.v1.batch_handlers:schedule
  name: batchFrameworkProcessing

- symbol: vllm_ascend.worker.model_runner_v1:NPUModelRunner.execute_model  # ⚡昇腾自己的执行器
  name: modelRunnerExec
  handler: ...v1.model_handlers:execute_model_runner
  domain: Execute

# ⚡执行时长采集的版本演进:旧 API 只到 0.14.0rc1,0.15.0rc1 起换上游新接口
- symbol: vllm_ascend.utils:ProfileExecuteDuration.capture_async
  min_version: "0.9.1"
  max_version: "0.14.0rc1"
  handler: ...v1.model_handlers:capture_async

- symbol: vllm.v1.utils:record_function_or_nullcontext
  min_version: "0.15.0rc1"
  handler: ...v1.model_handlers:record_function_or_nullcontext

配置文件(ms_service_profiler_config.json)常用字段:enable(0/1 总开关)、 prof_dir(默认 ~/.ms_server_profiler)、acl_task_time(算子下发/执行时延, 0/1/2/3 四档,3=Torch Profiler dump)、timelimit(进程自动停止时长,0-7200 秒)、 domain(限定 Request/KVCache/ModelExecute/BatchSchedule/Communication 域,减数据量)。 产物四件:chrome_tracing.json(Insight 打开)、request.csv、 kvcache.csv、batch.csv——服务级"请求→批→KV"全链路即可视化。

④ 版本演进与坑(联动风险清单)

⑤ 怎么选:一张决策图

这张图回答什么问题:拿到一个性能问题,先走哪条线。
图 5 · 选型决策(LR)
flowchart LR A["性能问题"] --> B{"瓶颈在算子/内核?
(Cube/Vector/通信占比)"} B -->|是| C["线一 Ascend PyTorch Profiler
kernel_details / op_statistic"] B -->|否| D{"瓶颈在服务流程?
(调度/批/KV/请求时延)"} D -->|是| E["线二 MS Service Profiler
batch.csv / kvcache.csv / request.csv"] D -->|不确定| F["先线二看全链路
再线一钻算子"] style C fill:#eef3ff,stroke:#5b6ee1 style E fill:#eef3ff,stroke:#5b6ee1

与半年目标的衔接:线一产出(kernel/operator/step_trace 三张 csv)正是 KR1.3 的 "profile 分解 Cube/Vector/通信占比"的输入;线二的 batch/kvcache/request 三张 csv 正好覆盖调度与 KV 工程的问题定位;两线数据在 MindStudio Insight 里同源查看。

⑥ 链接

首页