SGLang 在 AMD Instinct GPU 上的性能剖析实战:RPD 与 PyTorch Profiler 全流程指南
2026/9/10 10:33:44 网站建设 项目流程

SGLang 在 AMD Instinct GPU 上的性能剖析实战:RPD 与 PyTorch Profiler 全流程指南

【免费下载链接】sglangSGLang is a high-performance serving framework for large language models and multimodal models.项目地址: https://gitcode.com/GitHub_Trending/sg/sglang

本文档配套脚本与补丁位于仓库 3rdparty/amd/profiling 目录,是 AMD 官方为 SGLang 推理系统(Infer System)编写的一篇性能剖析(Profiling)应用说明(AppNote)。它面向搭载 AMD Instinct GPU 的 ROCm 环境,同时也兼容 NVIDIA/CUDA 环境,详细给出 RPD(ROCM Profile Data)与 PyTorch Profiler 两种剖析工具从安装、代码改造(patch)、服务启动到压测触发、结果可视化的完整闭环。读完本文,你将掌握:如何为 SGLang 服务端打上剖析补丁、如何用loadTracer.sh预加载 RPD 追踪器、如何通过/start_profile/stop_profileHTTP 接口精确控制剖析窗口,以及如何把 RPD 文件转换成 Perfetto 可加载的 JSON 并定位 GPU kernel、CPU 活动与负载不均衡等性能问题。

一、概述:两种剖析方法的选择

SGLang 是一个面向大语言模型与多模态模型的高性能推理服务框架。当需要在 AMD Instinct GPU 上定位性能瓶颈(例如 GPU kernel 耗时、跨进程负载不均衡、调度空隙 bubbles)时,3rdparty/amd/profiling/PROFILING.md 提供了两条主路径:

  1. RPD Profiler(rocmProfileData):低开销、跨平台的分析器。同一套代码注入不仅能在 ROCm/AMD GPU 上工作,也能在 CUDA/NVIDIA GPU 上工作,适合大规模、长时间采样。
  2. PyTorch Profiler:Torch 官方内置的剖析工具,适合快速查看算子级耗时,默认只记录 TP rank 0,可自行修改补丁扩大记录范围。

两种方法共用同一套服务端剖析控制接口(/start_profile/stop_profile),且文档明确提示:不要把 RPD 与 PyTorch Profiler 同时启用,以免互相干扰。

与当前仓库源码的对应关系

值得说明的是,原文档对应的是一套较早期的 SGLang 代码结构(patch 直接修改scheduler.pytokenizer_manager.pyserver.py)。在当前仓库中,SGLang 已将剖析能力收敛为独立的 SchedulerProfilerManager,并通过activities参数同时支持CPUGPURPDMEMCUDA_PROFILER等多种活动类型——其中"RPD"分支正是对本文所述 RPD 工作流的内建化封装(见 profiler_manager.py),rpdTracerControlRocpdSchemarpd_to_chrome_trace等关键符号与文档中的手工注入代码一一对应。因此本文既保留原文档可直接复现的手工 patch 流程,也会在相应小节对照当前源码说明演进后的用法。

二、RPD Profiler 剖析 SGLang:完整分步流程

2.1 安装 RPD(带 rpd.patch)

首先在容器内安装系统依赖,并用补丁修正 RPD 的构建配置。执行 install_rpd.sh 中的命令:

# download and install RPD apt update && apt install -y sqlite3 libsqlite3-dev libfmt-dev # install rpd module git clone https://github.com/ROCmSoftwarePlatform/rocmProfileData cd rocmProfileData git checkout 976899e9c6dbc6dd2bccf770818e4e44125590ac git apply rpd.patch make && make install cd rocpd_python && python setup.py install && cd .. cd rpd_tracer && make clean;make install && python setup.py install && cd ..

其中git checkout 976899e9c6dbc6dd2bccf770818e4e44125590ac将 rocmProfileData 固定到已验证的提交,保证补丁可干净应用。rpd.patch的内容如下,它把rpd_tracer的 ROCm 构建源文件从RoctracerDataSource.cpp RocmSmiDataSource.cpp缩减为仅保留RoctracerDataSource.cpp

diff --git a/rpd_tracer/Makefile b/rpd_tracer/Makefile index e9d9feb..b2e9e1a 100644 --- a/rpd_tracer/Makefile +++ b/rpd_tracer/Makefile @@ -16,7 +16,7 @@ ifneq (,$(HIP_PATH)) $(info Building with roctracer) RPD_LIBS += -L/opt/rocm/lib -lroctracer64 -lroctx64 -lamdhip64 -lrocm_smi64 RPD_INCLUDES += -I/opt/rocm/include -I/opt/rocm/include/roctracer -I/opt/rocm/include/hsa - RPD_SRCS += RoctracerDataSource.cpp RocmSmiDataSource.cpp + RPD_SRCS += RoctracerDataSource.cpp RPD_INCLUDES += -D__HIP_PLATFORM_AMD__ endif

去掉RocmSmiDataSource.cpp意味着编译时不再引入 ROCm SMI 数据源,从而规避该组件在特定环境下导致的构建或运行问题。安装完成后,rocpd.schemapython -m rocpd.schema)、rpdTracerControllibrpd_tracer.so将可供调用。

2.2 部署 loadTracer.sh 预加载追踪器

将 loadTracer.sh 放到python/sglang目录(原文档为/sglang/python/sglang,在当前仓库中对应python/sglang)。该脚本的核心职责是:创建 RPD 追踪数据库、设置环境变量、并通过LD_PRELOAD预加载librpd_tracer.so,使目标进程在启动瞬间即被注入追踪能力:

#!/bin/bash ################################################################################ # Copyright (c) 2021 - 2023 Advanced Micro Devices, Inc. All rights reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. ################################################################################ OUTPUT_FILE="trace.rpd" if [ "$1" = "-o" ] ; then OUTPUT_FILE=$2 shift shift fi if [ -e ${OUTPUT_FILE} ] ; then rm ${OUTPUT_FILE} fi python3 -m rocpd.schema --create ${OUTPUT_FILE} if [ $? != 0 ] ; then echo "Error: Could not create rpd file. Please run 'python setup.py install' from the rocpd_python dir" exit fi export RPDT_FILENAME=${OUTPUT_FILE} export RPDT_AUTOSTART=0 LD_PRELOAD=librocm-smi_64:librpd_tracer.so "$@"

要点说明:

  • OUTPUT_FILE默认trace.rpd,可用loadTracer.sh -o my.trace.rpd ...覆盖;
  • python3 -m rocpd.schema --create负责按 RPD 的 SQLite schema 初始化追踪数据库文件,若失败会提示先执行rocpd_python目录下的python setup.py install
  • RPDT_AUTOSTART=0表示追踪不随进程启动而自动开始,而是等待代码中的rpdTracerControl显式触发——这正是后面start_profile/stop_profile控制剖析窗口的机制基础;
  • LD_PRELOAD同时预加载librocm-smi_64librpd_tracer.so

2.3 打补丁:注入 RPD 剖析代码(基础版)

使用git apply rpd_profile_server_enable.patch应用补丁。该补丁的核心目的是让 SGLang 原有的start_profile/stop_profile剖析钩子改走 RPD 路径,主要修改 python/sglang/srt/managers/scheduler.py:

diff --git a/python/sglang/srt/managers/scheduler.py b/python/sglang/srt/managers/scheduler.py index 62d1ff9..9021c01 100644 --- a/python/sglang/srt/managers/scheduler.py +++ b/python/sglang/srt/managers/scheduler.py @@ -71,6 +71,8 @@ from sglang.srt.utils import ( suppress_other_loggers, ) from sglang.utils import get_exception_traceback +from rpdTracerControl import rpdTracerControl +rpdTracerControl.skipCreate() + logger = logging.getLogger(__name__) @@ -245,6 +247,7 @@ class Scheduler: ], with_stack=True, ) + self.rpd = rpdTracerControl() @torch.inference_mode() def event_loop(self): @@ -1027,15 +1030,24 @@ class Scheduler: def start_profile(self) -> None: if self.profiler is None: raise RuntimeError("Profiler is not enabled.") - self.profiler.start() + #self.profiler.start() #block pytorch profiler for rpd profiler enabling + if self.tp_rank == 0 or self.tp_rank == 1: + self.rpd.start() + self.rpd.rangePush("", "rpd profile range", "") + logger.info("rpd is enabled") def stop_profile(self) -> None: if self.profiler is None: raise RuntimeError("Profiler is not enabled.") - self.profiler.stop() - self.profiler.export_chrome_trace( - self.torch_profiler_trace_dir + "/" + str(time.time()) + ".trace.json.gz" - ) + #self.profiler.stop() + #self.profiler.export_chrome_trace( + # self.torch_profiler_trace_dir + "/" + str(time.time()) + ".trace.json.gz" + #) + if self.tp_rank ==0 or self.tp_rank ==1: + self.rpd.rangePop() + self.rpd.stop() + self.rpd.flush() + logger.info("rpd is done") logger.info("Profiler is done")

代码注入要点:

  • 模块加载时执行rpdTracerControl.skipCreate(),跳过 RPD 默认的自动创建逻辑,避免与loadTracer.sh预先创建的trace.rpd冲突;
  • Scheduler 初始化时创建self.rpd = rpdTracerControl()
  • start_profile注释掉self.profiler.start()(即禁用 PyTorch Profiler),改为仅在tp_rank == 0 or tp_rank == 1两个 rank 上执行self.rpd.start()rangePush一个命名区间"rpd profile range"
  • stop_profile中注释掉 PyTorch Profiler 的 stop 与 chrome trace 导出,改为在这两个 rank 上执行rangePop()stop()flush()
Common Notes 1:为什么只记录 2 个 rank?

文档特别强调:示例使用 TP=8,但补丁故意只在 tp_rank=0/1 上记录 RPD 剖析。原因是 Perfetto 的流式加载模式最多只能可视化约 8GB 的 JSON 文件;只记录 2 个 rank,一方面仍能检查 rank 之间是否存在问题(例如负载不均衡 load imbalance、NCCL 通信问题),另一方面可以在 JSON 文件达到 8GB 之前记录相对更长的剖析时长。

2.4 高级调试:启用 CPU/Python 活动追踪

当需要定位跨 GPU 进程的负载不均衡根因、调度空隙(bubbles)等棘手问题时,仅靠 GPU kernel 信息不够,需要捕获更多 CPU 与 Python 活动。此时应改用git apply rpd_profile_server_enable_wCPU_activities.patch,该补丁共修改 3 个文件,分别为 python/sglang/srt/managers/scheduler.py、python/sglang/srt/managers/tokenizer_manager.py 与 server 入口(对应历史版本中的 python/sglang/srt/server.py 位置,当前仓库已重构,参见下文演进说明):

diff --git a/python/sglang/srt/managers/scheduler.py b/python/sglang/srt/managers/scheduler.py index 62d1ff9..2edb427 100644 --- a/python/sglang/srt/managers/scheduler.py +++ b/python/sglang/srt/managers/scheduler.py @@ -71,6 +71,8 @@ from sglang.srt.utils import ( suppress_other_loggers, ) from sglang.utils import get_exception_traceback +from rpdTracerControl import rpdTracerControl +rpdTracerControl.skipCreate() logger = logging.getLogger(__name__) @@ -245,6 +247,7 @@ class Scheduler: ], with_stack=True, ) + self.rpd = rpdTracerControl() @torch.inference_mode() def event_loop(self): @@ -1027,15 +1030,26 @@ class Scheduler: def start_profile(self) -> None: if self.profiler is None: raise RuntimeError("Profiler is not enabled.") - self.profiler.start() + #self.profiler.start() + logger.info("torch profiler is disabled") + if self.tp_rank == 0 or self.tp_rank == 1: + self.rpd.setPythonTrace(True) + self.rpd.start() + self.rpd.rangePush("", "scheduler", "") + logger.info("rpd is enabled inside scheduler profiling") def stop_profile(self) -> None: if self.profiler is None: raise RuntimeError("Profiler is not enabled.") - self.profiler.stop() - self.profiler.export_chrome_trace( - self.torch_profiler_trace_dir + "/" + str(time.time()) + ".trace.json.gz" - ) + #self.profiler.stop() + #self.profiler.export_chrome_trace( + # self.torch_profiler_trace_dir + "/" + str(time.time()) + ".trace.json.gz" + #) + if self.tp_rank ==0 or self.tp_rank ==1: + self.rpd.rangePop() + self.rpd.stop() + self.rpd.flush() + logger.info("rpd is done inside scheduler") logger.info("Profiler is done")

与基础版的差异在于调用self.rpd.setPythonTrace(True)开启 Python 调用栈追踪,并将 range 命名为"scheduler"。同时该补丁在 tokenizer_manager 与 server 入口也注入追踪,形成server → tokenizer_manager → scheduler三层 span:

diff --git a/python/sglang/srt/managers/tokenizer_manager.py b/python/sglang/srt/managers/tokenizer_manager.py index 2621ccd..181df85 100644 --- a/python/sglang/srt/managers/tokenizer_manager.py +++ b/python/sglang/srt/managers/tokenizer_manager.py @@ -58,6 +58,10 @@ from sglang.srt.sampling.sampling_params import SamplingParams from sglang.srt.server_args import PortArgs, ServerArgs from sglang.srt.utils import is_generation_model, is_multimodal_model +from rpdTracerControl import rpdTracerControl +rpdTracerControl.skipCreate() + + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) logger = logging.getLogger(__name__) @@ -514,10 +518,20 @@ class TokenizerManager: self.send_to_scheduler.send_pyobj(req) def start_profile(self): + rpd = rpdTracerControl() + rpd.setPythonTrace(True) + rpd.start() + rpd.rangePush("", "tokenizer_manager", "") + logger.info("tokenizer_manager rpd profiling started!") req = ProfileReq.START_PROFILE self.send_to_scheduler.send_pyobj(req) def stop_profile(self): + rpd = rpdTracerControl() + rpd.rangePop() + rpd.stop() + rpd.flush() + logger.info("rpd profiling is done inside tokenizer_manager!") req = ProfileReq.STOP_PROFILE self.send_to_scheduler.send_pyobj(req)

server 入口部分(历史版本 python/sglang/srt/server.py 中的 HTTP 处理器,当前仓库对应 http_server.py 的/start_profile/stop_profile路由):

diff --git a/python/sglang/srt/server.py b/python/sglang/srt/server.py index 7111c93..2bd722c 100644 --- a/python/sglang/srt/server.py +++ b/python/sglang/srt/server.py @@ -30,6 +30,8 @@ import threading import time from http import HTTPStatus from typing import Dict, List, Optional, Union +from rpdTracerControl import rpdTracerControl +rpdTracerControl.skipCreate() # Fix a bug of Python threading setattr(threading, "_register_atexit", lambda *args, **kwargs: None) @@ -152,6 +154,11 @@ async def flush_cache(): @app.post("/start_profile") async def start_profile(): """Start profiling.""" + rpd = rpdTracerControl() + rpd.setPythonTrace(True) + rpd.start() + rpd.rangePush("", "server rpd profile range", "") + logger.info("rpd profiling started in server.py!") tokenizer_manager.start_profile() return Response( content="Start profiling.\n", @@ -164,6 +171,11 @@ async def start_profile(): async def stop_profile(): """Stop profiling.""" tokenizer_manager.stop_profile() + rpd = rpdTracerControl() + rpd.rangePop() + rpd.stop() + rpd.flush() + logger.info("rpd profiling is done in server.py!") return Response( content="Stop profiling. This will take some time.\n", status_code=200,

注意:三个进程/协程各自创建rpdTracerControl()实例并开启追踪,意味着 CPU 追踪会覆盖整个请求链路——从 HTTP 入口、tokenizer 预处理到 scheduler 的 batch 调度与模型前向。这在排查"瓶颈到底出在调度还是 kernel 执行"时非常有用。

2.5 准备示例模型:dummy_grok1

文档以 grok1 剖析为例,在3rdparty/amd/profiling目录内创建一个dummy_grok1目录,内含config.json(若使用仓库提供的 server.sh,需把该目录复制到--model-path指向的路径):

cat ../dummy_grok1/config.json { "architectures": [ "Grok1ModelForCausalLM" ], "embedding_multiplier_scale": 78.38367176906169, "output_multiplier_scale": 0.5773502691896257, "vocab_size": 131072, "hidden_size": 6144, "intermediate_size": 32768, "max_position_embeddings": 8192, "num_experts_per_tok": 2, "num_local_experts": 8, "num_attention_heads": 48, "num_hidden_layers": 64, "num_key_value_heads": 8, "head_dim": 128, "rms_norm_eps": 1e-05, "rope_theta": 10000.0, "model_type": "mixtral", "torch_dtype": "bfloat16" }

该配置文件描述了一个 MoE 结构的 64 层 Transformer(model_typemixtral,8 个本地专家、每 token 激活 2 个专家),配合--load-format dummy--quantization fp8使用,无需真实权重即可启动服务用于剖析验证。

2.6 启动服务端(server.sh)

在 docker 容器的第一个终端中,用 RPD 启用的脚本启动服务:server.sh

#!/bin/bash # export SGLANG_TORCH_PROFILER_DIR=/data/sglang/ export SGLANG_TORCH_PROFILER_DIR=/sgl-workspace/sglang/profile/ # Get the current timestamp TIMESTAMP=$(date +"%Y%m%d_%H%M%S") # Define the log file with a timestamp LOGFILE="sglang_server_log_$TIMESTAMP.json" # Run the Python command and save the output to the log file loadTracer.sh python3 -m sglang.launch_server \ --model-path /sgl-workspace/sglang/dummy_grok1 \ --tokenizer-path Xenova/grok-1-tokenizer \ --load-format dummy \ --quantization fp8 \ --tp 8 \ --port 30000 \ --disable-radix-cache 2>&1 | tee "$LOGFILE"

关键启动参数说明:

参数含义
loadTracer.sh前置包装通过LD_PRELOAD注入 RPD 追踪器,是 RPD 剖析的必需环节
--model-pathdummy_grok1 目录使用步骤 2.5 的虚拟模型目录(记得改成实际路径
--tokenizer-pathXenova/grok-1-tokenizergrok1 的分词器
--load-formatdummy不加载真实权重,快速拉起服务
--quantizationfp8启用 FP8 量化路径
--tp88 卡张量并行
--port30000服务端口,供后续 curl 剖析接口与压测使用
--disable-radix-cache关闭 radix cache,简化剖析场景

同时导出SGLANG_TORCH_PROFILER_DIR指定默认剖析输出目录(该环境变量在 environ.py 中定义,默认值为/tmp,当前 profiler 实现中output_dir is None时会回退读取该变量,见 profiler_manager.py)。

Common Notes 2
  • 记得把--model-path改为正确的模型路径;
  • RPD 剖析必须通过loadTracer.sh启动;
  • SGLANG_TORCH_PROFILER_DIR供默认 PyTorch Profiler 使用;
  • 如果使用 PyTorch Profiler,则不要使用 loadTracer.sh,直接python3 -m sglang.launch_server即可。

2.7 压测客户端触发剖析(client.sh)

等到服务端终端出现The server is fired up and is ready to roll!后,在同一容器的另一个终端运行 client.sh:

#!/bin/bash # Start profiling via API curl http://localhost:30000/start_profile -H "Content-Type: application/json" # Benchmark serving using sglang with random dataset and tokenizer # Define the log file with a timestamp TIMESTAMP=$(date +%Y%m%d_%H%M%S) LOGFILE="sglang_client_log_$TIMESTAMP.json" # Run the benchmark with specified parameters and save logs python3 -m sglang.bench_serving \ --backend sglang \ --tokenizer Xenova/grok-1-tokenizer \ --dataset-name random \ --random-input 1024\ --random-output 1024 \ --num-prompts 240 \ --request-rate 8 \ --output-file online.jsonl 2>&1 | tee "$LOGFILE" # Stop profiling via API curl http://localhost:30000/stop_profile -H "Content-Type: application/json" # Convert tracing file to csv & json sqlite3 trace.rpd ".mode csv" ".header on" ".output trace.csv" "select * from top;" ".output stdout" python3 /sgl-workspace/rocmProfileData/tools/rpd2tracing.py trace.rpd trace.json

流程拆解:

  1. curl /start_profile通过 HTTP 接口开启剖析(服务端收到后,注入的 RPD 代码在指定 rank 上start()+rangePush);
  2. 运行 sglang.bench_serving 发起在线压测:random数据集、输入输出各 1024 token、240 个请求、请求速率 8 req/s,日志与结果分别存入带时间戳的sglang_client_log_*.jsononline.jsonl
  3. 压测结束后curl /stop_profile关闭剖析并 flush 数据;
  4. sqlite3trace.rpdtop表导出为 CSV,再用rocmProfileData/tools/rpd2tracing.py将 RPD 文件转换为 Perfetto 可加载的trace.json
Common Notes 3
  • 使用curl http://localhost:30000/start_profilecurl http://localhost:30000/stop_profile控制剖析起止;更多细节可查看 python/sglang/srt/managers/scheduler.py(当前仓库中的剖析逻辑已迁移至 profiler_manager.py);
  • 不要将 RPD Profiler 与 PyTorch Profiler 一起使用,避免相互干扰;
  • rocmProfileData/tools/rpd2tracing.py负责从 RPD 文件生成 JSON 文件。

2.8 可视化:Perfetto 加载大 JSON

文档第 7 步建议参照 Perfetto 的大 trace 可视化指南加载超大 JSON 文件,并尽量调整参数使 trace.json 小于 9GB。实践中可通过减少--num-prompts、缩短剖析窗口、或沿用"只记录 2 个 rank"的策略来控制 JSON 体积。

三、PyTorch Profiler 剖析 SGLang:分步流程

当只需要算子级耗时统计、无需跨进程 CPU/Python 调用栈时,PyTorch Profiler 是更轻量的选择。步骤如下:

3.1 应用 torch_profiler.patch

diff --git a/python/sglang/srt/managers/scheduler.py b/python/sglang/srt/managers/scheduler.py index 62d1ff9..6ecd78c 100644 --- a/python/sglang/srt/managers/scheduler.py +++ b/python/sglang/srt/managers/scheduler.py @@ -240,7 +240,6 @@ class Scheduler: ) self.profiler = torch.profiler.profile( activities=[ - torch.profiler.ProfilerActivity.CPU, torch.profiler.ProfilerActivity.CUDA, ], with_stack=True, @@ -1033,9 +1032,11 @@ class Scheduler: if self.profiler is None: raise RuntimeError("Profiler is not enabled.") self.profiler.stop() - self.profiler.export_chrome_trace( - self.torch_profiler_trace_dir + "/" + str(time.time()) + ".trace.json.gz" - ) + if self.tp_rank == 0: + with open(f"stats_repro_{int(time.time())}.txt", "w") as f: + print(self.profiler.key_averages(group_by_input_shape=True).table(sort_by="cuda_time_total", row_limit=-1), file=f) + print("Profiling stats done.") + logger.info("Profiler is done")

补丁作用:

  • torch.profiler.profileactivities移除 CPU 活动,只保留ProfilerActivity.CUDA(降低剖析开销与数据量);
  • 不再导出 chrome trace JSON,改为在TP rank 0上将key_averages(group_by_input_shape=True)的统计表(按cuda_time_total排序,全量行)写入stats_repro_<时间戳>.txt

文档提示:可以修改补丁中的if self.tp_rank == 0,让更多 rank 参与记录。

3.2~3.4 其余步骤

  1. 创建模型路径目录(复用 2.5 的 dummy_grok1 配置),复制到--model-path指定位置;
  2. 修改 server.sh,去掉loadTracer.sh(改为直接python3 -m sglang.launch_server ...),在容器内一个终端启动;
  3. 参照 RPD 一节第 6 步运行修改后的 client.sh,但删除最后两行(RPD 转 CSV/JSON 的命令),因为本次产物是stats_repro_*.txt统计表而非 RPD 文件。

四、当前仓库中的内建剖析实现(源码级对照)

原文档的 patch 基于早期代码。在当前仓库中,SGLang 已把上述工作流内建到 SchedulerProfilerManager,剖析请求经 HTTP 路由(http_server.py 的/start_profile/stop_profile)下发到 scheduler 的_profile()分发(profiler_manager.py)。

  • RPD 内建支持:当activities包含"RPD"时,当前实现自动完成rpdTracerControl.skipCreate()、在 TP rank 0 用RocpdSchema初始化trace.rpdsetPythonTrace(True)+start()+rangePush,停止时rangePop/stop/flush,并由 rank 0 调用rpd_to_chrome_trace输出rpd-<时间戳>-TP-<rank>.trace.json.gz(见 profiler_manager.py 与_stop_profile的 RPD 分支 L361-L372)。这与文档手工注入的代码一一对应,只是改由框架统一管理;
  • PyTorch Profiler 内建支持:默认activities = ["CPU", "GPU"],映射到torch.profiler.ProfilerActivity.CPU/CUDA,支持with_stackrecord_shapes,并按DP/TP/PP/EP秩命名输出<profile_id>-TP-<rank>.trace.json.gz(profiler_manager.py 与 L335-L358);
  • 按阶段剖析profile_by_stage模式可分别捕获 prefill(EXTEND)与 decode 阶段的 trace(profiler_manager.py),并支持SGLANG_PROFILE_BY_STAGE_DECODE_MIN_BS等环境变量控制 decode 捕获的最小 batch;
  • 内存剖析activities"MEM"时启用torch.cuda.memory._record_memory_history并导出 memory snapshot(profiler_manager.py)。

因此,在较新的 SGLang 版本上做剖析时,可优先尝试通过启动参数/请求体直接传入activities=["RPD"]["CPU","GPU"],无需手工打补丁;原文档的 patch 流程则适用于对旧版本或定制化剖析需求做深度改造。

五、常见问题与注意事项汇总

  1. RPD 必须由 loadTracer.sh 启动,否则librpd_tracer.so未被预加载,rpdTracerControl无法工作;PyTorch Profiler 场景则必须去掉它;
  2. 两种剖析器不要混用,避免重复注入与数据互相干扰;
  3. 控制 trace 体积:Perfetto 流式加载上限约 8GB,JSON 建议小于 9GB;通过只记录 tp_rank 0/1、缩短剖析窗口、减少请求数来控制;
  4. 检查 RPD 文件可读性:若提示Error: Could not create rpd file,请确认已在rocpd_python目录执行python setup.py install
  5. 模型路径--model-path指向的目录需包含有效config.json(如 dummy_grok1),并注意 tokenizer 路径的可用性;
  6. trace 文件转换:RPD 产物使用rocmProfileData/tools/rpd2tracing.py trace.rpd trace.json转换,PyTorch Profiler 产物为stats_repro_<时间戳>.txt统计表,两者后续处理方式不同。

六、参考资料

  • 本目录所有脚本与补丁:3rdparty/amd/profiling(含 install_rpd.sh、rpd.patch、loadTracer.sh、rpd_profile_server_enable.patch、rpd_profile_server_enable_wCPU_activities.patch、server.sh、client.sh、torch_profiler.patch)
  • 剖析控制接口入口:http_server.py
  • 剖析调度实现:profiler_manager.py
  • 剖析输出目录环境变量SGLANG_TORCH_PROFILER_DIR:environ.py
  • 压测工具:bench_serving.py

【免费下载链接】sglangSGLang is a high-performance serving framework for large language models and multimodal models.项目地址: https://gitcode.com/GitHub_Trending/sg/sglang

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询