Skip to content

[Metax][Optimization] 优化 PaddleOCR-VL 在 Metax GPU 上的视觉路径开销#7619

Open
Dryoung95 wants to merge 1 commit intoPaddlePaddle:developfrom
Dryoung95:codex/opt5-metax-paddleocr-vl
Open

[Metax][Optimization] 优化 PaddleOCR-VL 在 Metax GPU 上的视觉路径开销#7619
Dryoung95 wants to merge 1 commit intoPaddlePaddle:developfrom
Dryoung95:codex/opt5-metax-paddleocr-vl

Conversation

@Dryoung95
Copy link
Copy Markdown

背景

本 PR 面向 PaddleOCR-VL 在 Metax GPU 上的推理路径做小步性能优化,目标是降低视觉分支的额外开销,提升实际服务场景下的响应效率。

在前期 profiling 与多轮实验中,热点持续集中在 extract_vision_features_paddleocr() 邻近路径,尤其是视觉特征提取后的元数据组织、位置编码准备以及 projector 邻近的数据流处理。因此,本次提交只保留已经验证通过、风险较低、收益可复现的一组改动。

问题现象

在 Metax GPU 上运行 PaddleOCR-VL 时,视觉路径存在以下额外开销:

  • 视觉分支中部分元数据在 host/device 间存在不必要同步
  • FD_ENABLE_MAX_PREFILL=1 主路径下,projector 邻近数据流仍有可精简空间
  • 这部分开销在小并发场景下会放大,影响整体响应时间

前期多轮实验表明:

  • 冷单请求路径可优化,但收益通常较弱
  • 重复图片/小并发路径对视觉分支额外开销更敏感
  • 必须避免引入新的运行时不稳定问题

本次改动

本 PR 仅保留最终确认可用的一组最小改动,涉及以下文件:

  • fastdeploy/model_executor/models/paddleocr_vl/projector.py
  • fastdeploy/model_executor/models/paddleocr_vl/siglip.py
  • fastdeploy/model_executor/models/paddleocr_vl/siglip_ops.py
  • fastdeploy/worker/metax_model_runner.py

最终保留的核心优化点是:

  1. 保持视觉主路径稳定,不引入高风险 packed projector 运行时路径
  2. extract_vision_features_paddleocr() 中,projector 直接复用 worker 已有的 host grid_thw_lst
  3. 去掉 FD_ENABLE_MAX_PREFILL=1 主路径里一处固定的 tensor -> cpu().numpy() 元数据同步
  4. 保持现有视觉主干与 projector 数学逻辑不变,只减少不必要的数据组织与搬运开销

验证结果

功能与稳定性

已完成以下验证:

  • 服务可正常启动
  • /health 返回 200
  • /v1/models 返回正常
  • 单请求 OCR 正常返回
  • 4x8 小并发请求可稳定完成
  • 未复现此前实验中出现的启动期 SIGBUS、请求长时间不返回、客户端超时断开等问题

性能结果

基于 Opt3 稳定基线进行对照,本次保留方案(Opt5)表现如下:

  • 单请求:7.4039s -> 7.3870s,约 -0.23%
  • 4x8 小并发 average:约 -6.07%
  • 4x8 小并发 P50:约 -5.97%
  • 4x8 小并发 P95:约 -9.73%

整体结论:

  • 单请求收益较小,但方向为正
  • 小并发收益更明确
  • 在收益存在的同时,没有引入新的稳定性问题

profiling 结论

在保留方案的并发专项复验中:

  • wall-clock 改善是可复现的
  • 热点仍集中在 extract_vision_features_paddleocr() 邻近路径
  • 但归一化后,热点占比相较基线已下降,说明这次改动确实打在原热点路径上,而不是偶然波动

因此,本 PR 的定位是:

  • 一个低风险、小步收益、可稳定运行的优化 patch
  • 不追求一次性大幅改写视觉主干
  • 为后续继续深挖视觉冷路径与并发路径优化提供稳定基线

风险与限制

本 PR 也有明确边界:

  • 当前收益属于“小步优化”,不是大幅提速 patch
  • 单请求路径收益弱于并发路径
  • 热点虽然下降,但视觉主路径仍然是后续优化重点
  • 一些更激进的 packed 路线尝试虽然可能进一步减少数据组织开销,但当前存在运行时稳定性风险,因此本 PR 不引入这些高风险实验性改动

后续计划

后续会继续围绕以下方向推进:

  • 视觉冷路径主干进一步优化
  • extract_vision_features_paddleocr() 邻近数据流继续压缩
  • 更细粒度地拆解 Siglip / projector 邻近张量组织开销
  • 在稳定基线之上继续寻找更强收益点

说明

本 PR 只提交已验证通过且建议保留的代码改动,不包含前期实验分支中的高风险尝试与临时 profiling 产物。

@paddle-bot
Copy link
Copy Markdown

paddle-bot Bot commented Apr 24, 2026

Thanks for your contribution!

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 24, 2026

CLA assistant check
All committers have signed the CLA.

@Dryoung95 Dryoung95 force-pushed the codex/opt5-metax-paddleocr-vl branch from c6b3374 to 162c6f5 Compare April 24, 2026 16:54
@Dryoung95
Copy link
Copy Markdown
Author

目前没有看到编译失败、测试失败或 smoke test 失败的代码级证据,更像是 runner / Jenkins remoting 层问题。

麻烦帮忙 rerun 一下这条检查。

Copy link
Copy Markdown

@PaddlePaddle-bot PaddlePaddle-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI Code Review | 2026-04-25 00:59:14

📋 Review 摘要

PR 概述:针对 PaddleOCR-VL 在 Metax GPU 上的视觉推理路径做小步性能优化,通过减少不必要的 host/device 数据同步、合并视觉特征处理、引入位置编码 LFU 缓存等手段降低视觉分支开销。
变更范围paddleocr_vl/projector.pypaddleocr_vl/siglip.pypaddleocr_vl/siglip_ops.pyworker/metax_model_runner.py
影响面 TagModels Metax


问题

级别 文件 概述
🟡 建议 siglip.py:339-364 SiglipEncoderLayer.forward 中 batch=1 快速路径与通用路径逻辑完全重复
❓ 疑问 siglip_ops.py:39 apply_rotary_pos_emb_vision 精度职责转移后缺少说明,新调用点可能静默引入精度损失

正确性核实

以下改动经代码库探索验证,均为预期行为:

  1. SiglipMLP.forward 去掉 hidden_states[0]:是一处 bug fix。原本 [0] 在 3D 输入 [1, seq, D] 时取 batch=0(正确),但经 SiglipEncoderLayer 的 batch=1 快速路径 squeeze 后变为 2D [seq, D],再做 [0] 就只取第一行 token(错误)。移除 [0] 后行为正确。✓

  2. 位置编码改用 fetch_position_embedding_lfu_cache(embeddings, h, w)(传全量 tensor)interpolate_pos_encoding 仅使用 embeddings.shape[-1](隐藏维度 D),不依赖 embeddings 的具体内容,因此传全量 tensor 与传切片结果完全一致。✓

  3. mm_num_token_func 接受 tuplepaddleocr_vl_processormm_num_tokens 实现中对 grid_thw 做了 isinstance(paddle.Tensor) 检测,非 Tensor 时直接 map(int, thw) 处理,支持 tuple 输入。✓

  4. _build_merge_permutation 替换 einops rearrange:numpy 手工 transpose (0,1,3,2,4) 等价于原 rearrange (t h p1 w p2) -> (t h w) (p1 p2 d) 的 merge 逻辑,数学语义一致。✓


总体评价

整体改动方向清晰,优化点均有可复现的 profiling 支撑,功能验证充分。SiglipEncoderLayer 中的代码重复是后续维护的主要隐患,建议在合入前或后续 PR 中提取公共方法消除重复;其余为低风险疑问,不阻塞合入。


hidden_states_out = residual + mlp_out

return (hidden_states_out.unsqueeze(0),)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 建议 SiglipEncoderLayer.forward batch=1 快速路径与通用路径逻辑完全重复

第 339–364 行(快速路径)与第 366–391 行(通用路径)计算逻辑一模一样,仅返回时多了 unsqueeze(0)。如后续有人修改通用路径(如新增 dropout、调整 layernorm 位置)而忘记同步快速路径,容易引入难以发现的不一致。

建议提取为私有方法复用:

def _forward_2d(self, hidden_states_2d, ...):
    residual = hidden_states_2d
    x = self.self_attn(self.layer_norm1(hidden_states_2d), ...)
    hs_post_attn = residual + x
    out = hs_post_attn + self.mlp(self.layer_norm2(hs_post_attn))
    return out

def forward(self, hidden_states, ...):
    if hidden_states.dim() == 3 and hidden_states.shape[0] == 1:
        return (self._forward_2d(hidden_states[0], ...).unsqueeze(0),)
    ...

@@ -37,27 +37,33 @@ def rotate_half(x):


def apply_rotary_pos_emb_vision(x, cos, sin):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ 疑问 apply_rotary_pos_emb_vision 不再保证 float32 精度

原实现在函数内做了 x.astype("float32") → 计算 → cast back,现在精度保证完全转移到调用方(native_neox_rope_embedding)。当前仓库中该函数只在 native_neox_rope_embedding 中调用,行为正确。但函数签名/注释没有说明「调用方需保证 x 是 float32」的新约定,若后续出现新调用点,容易在 bfloat16 下静默引入精度损失。建议在函数 docstring 或注释中注明 x 需为 float32。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants