Skip to content

[release/0.3] Fix memory leak caused by c_pointers - #1435

Open
risemeup1111 wants to merge 1 commit into
PaddlePaddle:release/0.3from
risemeup1111:cherry-pick/1325/release/0.3
Open

[release/0.3] Fix memory leak caused by c_pointers#1435
risemeup1111 wants to merge 1 commit into
PaddlePaddle:release/0.3from
risemeup1111:cherry-pick/1325/release/0.3

Conversation

@risemeup1111

Copy link
Copy Markdown
Contributor

PR Category

User Experience

PR Types

Bug fixes

Description

Fix memory fragmentation caused by uncached c_pointers() in CUTLASS DSL scalar types.

Problem:
CUTLASS DSL scalar types (Int32, Float32, Float64, Float16, BFloat16, TFloat32 等) 的 c_pointers() 方法在每次 kernel 调用时都会创建新的 ctypes
对象。在训练循环中高频 kernel launch 场景下,这些短生命周期对象与框架长生命周期对象交替分配,导致 pymalloc arena 碎片化,进程 RSS 单调增长

Fix:
在 paddlefleet_ops/init.py 中添加 monkey-patch,对所有 CUTLASS DSL Numeric 子类的 c_pointers() 方法注入 per-type cache(dict keyed by
scalar value)。首次调用时缓存结果,后续相同值直接返回缓存,避免重复创建 ctypes 对象。

验证生效
在日志搜索是否存在:[cptr_cache] Result: patched 18 types

是否引起精度变化

Cherry-pick of #1325 to release/0.3.

Merged in dev: #1325

@PaddlePaddle-bot PaddlePaddle-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🤖 Paddle-CI-Agent | pr_review | 2026-07-09 15:49:10

📋 Review 摘要

PR 概述:在 paddlefleet_ops 导入阶段为 CUTLASS DSL Numeric 标量类型注入 __c_pointers__ per-type cache,减少高频 kernel launch 下 ctypes 对象反复创建。
变更范围packages/paddlefleet_ops/src/paddlefleet_ops/__init__.py
影响面 TagCustomOps PaddleFleetOps

问题

级别 文件 概述
🟡 建议 packages/paddlefleet_ops/src/paddlefleet_ops/__init__.py:34 CUTLASS patch 初始化失败会被完全静默吞掉
🟡 建议 packages/paddlefleet_ops/src/paddlefleet_ops/__init__.py:79 成功日志与 PR 描述中的验证搜索串不一致

📝 PR 规范检查

标题不符合 checklist §D1 的 Cherry-Pick 格式;描述已包含 §D2 要求的 PR CategoryPR TypesDescription 结构。

标题建议(可直接复制):

  • [Cherry-Pick][Bug fixes] Fix CUTLASS DSL c pointer cache leak(#1325)

总体评价

缓存思路本身与 PR 目标一致,但当前失败路径和验证日志会让补丁未生效或验证误判时缺少可观测性。建议修正后再以 PR 描述中的日志信号验证。

def _patch_cutlass_cptr_cache(maxsize=4096):
try:
from cutlass.base_dsl import typing as _typing
except Exception:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 建议 这里会把 cutlass.base_dsl.typing 导入过程中的任意异常静默吞掉。

当前补丁是否生效完全依赖这个导入;如果安装了 nvidia-cutlass-dsl 但其依赖或初始化异常,函数会直接返回 0,后面也不会打印失败原因,训练进程仍然带着未缓存的 __c_pointers__ 继续跑。按 checklist §C,except Exception 吞异常且无日志属于错误静默。

建议只把 ImportError / ModuleNotFoundError 作为 CUTLASS 不存在的正常路径;对其它异常至少记录 warning 后返回,或让异常暴露出来。

_patched_count = _patch_cutlass_cptr_cache()
if _patched_count:
logging.getLogger(__name__).warning(
f"[cptr_cache] Patched {_patched_count} CUTLASS DSL types"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 建议 这里的成功日志和 PR 描述要求的验证信号不一致。

PR 描述让使用者搜索 [cptr_cache] Result: patched 18 types,但代码实际输出 [cptr_cache] Patched {N} CUTLASS DSL types。按现状验证步骤会误判补丁未生效。

建议统一为描述中的固定格式,例如 "[cptr_cache] Result: patched {_patched_count} types",或同步更新 PR 描述中的搜索串。

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (release/0.3@4dd81f9). Learn more about missing BASE report.

Additional details and impacted files

Impacted file tree graph

@@               Coverage Diff               @@
##             release/0.3     #1435   +/-   ##
===============================================
  Coverage               ?   100.00%           
===============================================
  Files                  ?         1           
  Lines                  ?         3           
  Branches               ?         0           
===============================================
  Hits                   ?         3           
  Misses                 ?         0           
  Partials               ?         0           
Flag Coverage Δ
coverage_combine 100.00% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Paddle-CI-Bot

Copy link
Copy Markdown

PaddleFleet Log Analysis

Run #29007720824 · Attempt 1

日志分析报告

流水线: Integration test (H20, multi-card) — Job 86067341571
失败维度: 多卡 × H20 (CUDA 12.9, GPU Capability 9.0)

流水线名称 问题标签 修复建议 日志片段
GLM4.5 sft TCP store 通信失败 / libuv assertion glm45_sft 在 fleet.init 阶段 TCP store 断连(Connection reset by peer),进程退出码 -6(SIGABRT),检查 PR 是否改动了 __init__.py 中的初始化顺序或 c_pointer 释放时机 报错代码
GLM4.5 lora HFValidationError — 本地 checkpoint 路径误作 repo_id AutoConfig.from_pretrained('/workspace/glm45_fleet/checkpoints/glm_full_pp_ckpts') 将绝对路径传入 hf_hub,需在 resolve_file_path 中对本地绝对路径提前返回,跳过 hf_hub 验证 报错代码
GLM4.5 dpo HFValidationError — 同上,lora 阶段的 checkpoint 供 dpo 加载时复现 同 lora 修复方向,resolve_file_path 判断 repo_id 是否为本地绝对路径,若是则直接拼接 filename 报错代码
GLM4.5 pre-train (Grouped GEMM) NCCL unhandled cuda error — broadcast_mp_parameters 阶段 Grouped GEMM 在 fleet.distributed_modelbroadcast_mp_parameters 时触发 NCCL cuda error,结合 PR 改动 paddlefleet_ops/__init__.py 中 c_pointer 释放逻辑,排查是否提前释放了 NCCL 通信资源 报错代码

失败的测试 case:

Integration test (H20, multi-card) / GLM4.5 sft         — glm45_sft.sh         sft_exit_code=250
Integration test (H20, multi-card) / GLM4.5 lora        — glm45_lora.sh        lora_exit_code=241
Integration test (H20, multi-card) / GLM4.5 dpo         — glm45_dpo.sh         sft_exit_code=241
Integration test (H20, multi-card) / GLM4.5 pre-train (Grouped GEMM) — glm45_pt_grouped_gemm.sh  exit_code=241

根本原因分析:

PR #1435[release/0.3] Fix memory leak caused by c_pointers)修改了 packages/paddlefleet_ops/src/paddlefleet_ops/__init__.py 中 c_pointer 的生命周期管理,引入了以下两类回归:

  1. SFT / TCP store 崩溃libuv assertion uv__finish_close: Assertion handle->flags & UV_HANDLE_CLOSING failed,随后 TCP store 出现 Connection reset by peer,进程被 SIGABRT 终止(exit -6)。c_pointer 释放时机提前,可能在 fleet.init 使用 TCP store 期间释放了底层句柄。

  2. LoRA / DPO — HFValidationError:lora 阶段训练完毕保存了 checkpoint 至本地路径 /workspace/glm45_fleet/checkpoints/glm_full_pp_ckpts,dpo/lora 脚本随后将此绝对路径作为 model_name_or_path 传给 AutoConfig.from_pretrained,触发 huggingface_hub 的 repo_id 格式校验失败。根因是 lora 阶段未能正常完成(源于 SFT 失败的连锁效应),checkpoint 目录可能不完整或路径拼接逻辑出现变化。

  3. Grouped GEMM — NCCL unhandled cuda errorbroadcast_mp_parameters 时触发 NCCL cuda 错误,与 c_pointer 释放后底层 CUDA context 失效一致。

修复建议:

  1. paddlefleet_ops/__init__.py 中检查 c_pointer 释放时机,确保持有 TCP store 句柄、NCCL communicator 的 c_pointer 在 fleet.init 完成、通信组析构之后才释放,避免 use-after-free。
  2. paddleformers/utils/download/download.pyhf_try_to_load_from_cache 入口处,判断 repo_id 是否为绝对路径(os.path.isabs(repo_id)),若是则直接返回 os.path.join(repo_id, filename),跳过 hf_hub 校验,修复 lora/dpo 加载本地 checkpoint 的问题。
  3. NCCL_DEBUG=INFO 重跑 Grouped GEMM 用例,确认 NCCL 错误是否与 c_pointer 修复后消失,以区分独立问题还是连锁影响。

🔍 准确性记录:请点击评论底部 😊 图标,选择 👍(准确)或 👎(有误),将自动记录到 CI 监控系统

🔄 每次 Re-run 后自动更新

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.

5 participants