# PR #28308 完整报告

- 仓库：`sgl-project/sglang`
- 标题：[Intel GPU] add pytorch profiling support for XPU in bench offline throughput and enhance num steps
- 合并时间：2026-06-29 09:10
- 原文链接：http://prhub.com.cn/sgl-project/sglang/pull/28308

---

# 执行摘要

- 一句话：XPU profiling 支持与步数增强
- 推荐动作：值得精读。该 PR 展示了如何为 benchmark 工具统一添加硬件 profiling 支持，设计简洁，可扩展性强。关注点在于 `start_profile` 接口的设计是否兼容所有 backend。

# 功能与动机

需要在 Intel GPU（XPU）上对 offline_throughput 基准测试进行 profiling，以分析推理性能瓶颈。现有 profiling 仅支持 GPU/CUDA，且无法控制 profiling 的步数，导致 trace 文件过大或包含不关心的阶段。

# 实现拆解

1. **在 `BenchArgs` 类中新增字段**：在 `python/sglang/benchmark/offline_throughput.py` 的 `BenchArgs` 数据类中添加 `profile_activities: Tuple[str]`（默认 ("CPU", "GPU")）和 `profile_steps: Optional[int]`（默认 None），用于存储用户指定的 profiling 配置。
2. **添加 CLI 参数解析**：在 `add_cli_args` 静态方法中增加 `--profile-activities`（支持 CPU、GPU、CUDA_PROFILER、XPU 多选）和 `--profile-steps`（int 类型）参数，并更新帮助文档中的使用示例。
3. **修改 profiling 调用逻辑**：在 `throughput_test_once` 函数中，将原有的 `backend.start_profile()` 改为带参数调用 `backend.start_profile(num_steps=profile_steps, activities=profile_activities)`，并调整 `stop_profile` 的逻辑：当指定 `profile_steps` 时，profiler 由 backend 内部自动停止，无需显式调用 `stop_profile`；同时修复了 trace 文件监控逻辑，确保 `monitor_trace_file` 被正确调用。
4. **传递参数链路**：在 `throughput_test` 函数中将 `bench_args` 中的 `profile_activities` 和 `profile_steps` 传递给 `throughput_test_once`。

关键文件：
- `python/sglang/benchmark/offline_throughput.py`（模块 基准测试；类别 source；类型 core-logic；符号 BenchArgs.profile_activities, BenchArgs.profile_steps, throughput_test_once, throughput_test）: 唯一修改的文件，新增 profiling 参数和调用逻辑，核心变更所在。

关键符号：throughput_test_once, throughput_test, BenchArgs.add_cli_args

## 关键源码片段

### `python/sglang/benchmark/offline_throughput.py`

唯一修改的文件，新增 profiling 参数和调用逻辑，核心变更所在。

以下代码展示了 `BenchArgs` 中的新增字段、CLI 参数注册以及 `throughput_test_once` 中修改后的 profiling 调用逻辑。

```python
# 文件 : python/sglang/benchmark/offline_throughput.py

@dataclasses.dataclass
class BenchArgs:
    # ... 其他字段不变 ...
    profile: bool = False
    # 新增：profiling 活动类型，默认 CPU+GPU，支持 XPU
    profile_activities: Tuple[str] = ("CPU", "GPU")
    # 新增：profiling 步数，None 表示对整个 generate 过程进行 profiling
    profile_steps: Optional[int] = None
    skip_warmup: bool = False
    do_not_exit: bool = False
    prompt_suffix: str = ""
    return_logprob: bool = False
    logprob_start_len: int = -1

    @staticmethod
    def add_cli_args(parser: argparse.ArgumentParser):
        # ... 其他参数 ...
        parser.add_argument(
            "--profile",
            action="store_true",
            help="Use Torch Profiler. The endpoint must be launched with "
            "SGLANG_TORCH_PROFILER_DIR to enable profiler.",
        )
        # 新增：--profile-activities 参数，可指定多个活动（如 CPU GPU XPU）
        parser.add_argument(
            "--profile-activities",
            type=str,
            nargs="+",
            default=["CPU", "GPU"],
            choices=["CPU", "GPU", "CUDA_PROFILER", "XPU"],
            help="Profiler activities: CPU, GPU, XPU, CUDA_PROFILER. "
            "If CPU/GPU/XPU, use torch profiler. If CUDA_PROFILER, use CUDA profiler.",
        )
        # 新增：--profile-steps 参数，限制 profiling 的步数
        parser.add_argument(
            "--profile-steps",
            type=int,
            default=None,
            help="Number of steps to profile. If not specified, profiles all steps.",
        )


def throughput_test_once(
    ignore_eos: bool,
    extra_request_body: Dict,
    profile: bool,
    profile_activities=None,  # 新增参数
    profile_steps=None,       # 新增参数
    return_logprob: bool = False,
    logprob_start_len: int = -1,
):
    # ...
    if profile:
        assert "SGLANG_TORCH_PROFILER_DIR" in os.environ, \
            "Please set SGLANG_TORCH_PROFILER_DIR."
        os.makedirs(os.environ["SGLANG_TORCH_PROFILER_DIR"], exist_ok=True)
        known_files = None
        # 传入 num_steps 和 activities 以支持受限 profiling
        backend.start_profile(
            num_steps=profile_steps,
            activities=profile_activities,
        )
        if profile_steps:
            # 当指定步数时，预先记录已存在的文件，以便后续只提取新生成的文件
            dir = os.getenv("SGLANG_TORCH_PROFILER_DIR")
            known_files = set(os.listdir(dir))

    st = time.perf_counter()
    gen_out = backend.generate(
        # ...
    )
    # ...
    if profile:
        dir = os.getenv("SGLANG_TORCH_PROFILER_DIR")
        if not profile_steps:
            # 未指定步数时，需要手动停止 profiler 并记录文件
            known_files = set(os.listdir(dir))
            backend.stop_profile()
        # 监控新生成的 trace 文件并打印
        monitor_trace_file(known_files, dir)

```

# 评论区精华

Reviewer **yanbing-j**指出初始实现中 `--profile-start-step` 的默认值 `output_len // 2` 无效，且默认只 profiling 一步也不合理。随后又发现当设置 `profile_steps` 时，原来的 `stop_profile()` 和 `monitor_trace_file` 被遗漏。作者 **polisettyvarma**回应称当 `profile_steps` 传入时 backend 自行处理停止，并已补充 `monitor_trace_file`。最终 reviewer 同意移除 `--profile-start-step` 参数，仅保留 `--profile-steps`。

- --profile-start-step 参数的必要性 (design): 移除了 `--profile-start-step` 参数，仅保留 `--profile-steps`，并在文档中说明 `profile_steps` 用于控制 decode 步数。
- profile 生命周期管理：stop_profile 缺失 (correctness): 作者解释当 `profile_steps` 传参时，backend 内部自动停止 profiler，但仍需调用 `monitor_trace_file`。修复后将 `monitor_trace_file` 移到条件之外，确保无论是否指定步数都会执行。

# 风险与影响

- 风险：低风险。变更局限于基准测试脚本，不影响模型推理核心逻辑。但需注意：`start_profile` 的 `num_steps` 和 `activities` 参数的实现依赖于底层 backend（Runtime/Engine），若 backend 不支持这些参数可能导致运行时错误，但当前修改假设 backend 已支持或具有容错处理。
- 影响：**用户**：XPU 用户现在可以使用 `--profile-activities XPU` 进行 profiling，并通过 `--profile-steps N` 控制 profiling 步数，减少 trace 文件大小。**系统**：无影响。**团队**：为后续扩展其他硬件平台的 profiling 提供了可复用的参数模式。
- 风险标记：对外部 backend 接口的隐式依赖 , 单文件变更，无测试覆盖

# 关联脉络

- 暂无明显关联 PR