Fast Debug: VeRL example
导言
VeRL 基于ray的多进程管理,并结合 推理、训练等多个阶段。其E2E时间组成和如何加速都是待研究的课题。
进程繁琐¶
ray管理的总显存1k+,根本无法分析
(base) root@node-29-116 ~ [10:59:49]
> pstree 2769617
viztracer─┬─gcs_server───364*[{gcs_server}]
├─python3.11
├─python3.11───30*[{python3.11}]
├─python3.11─┬─python3.11
│ ├─ray-dashboard-D───47*[{ray-dashboard-D}]
│ ├─ray-dashboard-E───48*[{ray-dashboard-E}]
│ ├─ray-dashboard-J───47*[{ray-dashboard-J}]
│ ├─ray-dashboard-M───47*[{ray-dashboard-M}]
│ ├─ray-dashboard-N───49*[{ray-dashboard-N}]
│ ├─ray-dashboard-R───50*[{ray-dashboard-R}]
│ ├─ray-dashboard-S───47*[{ray-dashboard-S}]
│ ├─ray-dashboard-S───48*[{ray-dashboard-S}]
│ ├─ray-dashboard-T───47*[{ray-dashboard-T}]
│ └─49*[{python3.11}]
├─python3.11───27*[{python3.11}]
├─raylet─┬─python3.11───52*[{python3.11}]
│ ├─python3.11───28*[{python3.11}]
│ ├─8*[ray::AgentLoopW───455*[{ray::AgentLoopW}]]
│ ├─294*[ray::IDLE───129*[{ray::IDLE}]]
│ ├─7*[ray::RewardMana───135*[{ray::RewardMana}]]
│ ├─ray::RewardMana───136*[{ray::RewardMana}]
│ ├─ray::TaskRunner─┬─8*[ray::TaskRunner───2*[{ray::TaskRunner}]]
│ │ └─143*[{ray::TaskRunner}]
│ ├─ray::WorkerDict─┬─python3.11
│ │ ├─python3.11───17*[python3.11]
│ │ ├─ray::IDLE───12*[{ray::IDLE}]
│ │ └─267*[{ray::WorkerDict}]
│ ├─3*[ray::WorkerDict─┬─python3.11]
│ │ ├─python3.11───17*[python3.11]]
│ │ ├─ray::IDLE───12*[{ray::IDLE}]]
│ │ └─265*[{ray::WorkerDict}]]
│ ├─4*[ray::WorkerDict─┬─python3.11]
│ │ ├─python3.11───17*[python3.11]]
│ │ ├─ray::IDLE───12*[{ray::IDLE}]]
│ │ └─264*[{ray::WorkerDict}]]
│ ├─ray::vLLMHttpSe─┬─VLLM::EngineCor───11*[{VLLM::EngineCor}]
│ │ ├─python3.11
│ │ └─465*[{ray::vLLMHttpSe}]
│ └─169*[{raylet}]
└─137*[{viztracer}]
热点占用入手¶
端到端python热点堆栈监控器(sniffer)
背景:
- 当前工具门槛高: 当前的堆栈分析工具profiler,mstx都需要理解代码并插入监控;但是之前发现 ray 这种多进程的资源调度平台,代码特别复杂。
- 监控不全或者繁琐: 由于需要插入代码,对于VeRL场景,推理和训练的代码逻辑还比较明显,但是ray在阶段切换之间的耗时(某些情况下会占到e2e 30%),程序快速启动的时间(initmodel/ loadwieght/ dataloader数据预处理的时间), 要么就是不知道在哪里监控,要不就是分散繁琐。
- 工具不支持中断:程序中断的话,信息就没有了。
- 工具不支持实时历史堆栈信息:这些工具采集的堆栈信息,必须程序结束才能看,但面对实际应用场景,单步1h+,根本等不及,想能并行起来看。
- 这个sniffer能实时看已经采集的信息。
- 工具dump出来的结果文件总是容易偏大: verl场景端到端采集基本是不可能的,分段采集,推理部分也经常很大,所以最近verl还上了文档,介绍如何在推理vllm代码里的一个micro_batch里插入打印。但是这有两个问题,一、聚焦于更小的子模块,就丧失了对全局性能信息的感知;二、迁移性不足,vllm这样插入按照这个文档,那假如接入其他新推理后端比如LightX2V,怎么修改又需要时间研究。
- sniffer能控制工具的结果文件大小:既可以通过循环执行的间隔x来控制,也可以通过对原子化的dump结果文件进行切分和成倍稀疏筛选。
- 工具采集还会有时间膨胀问题,影响性能分布: 本来就是对程序热点好奇才采集profiling的,但是带堆栈的profiling会膨胀带来时间分布的偏移。
- sniffer能通过循环执行的间隔x来控制膨胀率
大致实现思路:
- 每x秒循环执行
- 查询显卡、CPU、IO占用热点线程的pid
- py-spy dump --pid {pid} 的结果保存
- 异步程序可视化,将dump已保存的结果里的时间、pid、线程name、堆栈提取出来,变成
chrome:://tracing可读的可视化json; 如果dump结果过多,由于dump的数据都是原子化带时间戳的,可以很简单的实现时间线上的切分和稀疏的可视化。
工具效果:
- 面对一个新场景的软件(Areal),都不需要E2E跑通也不需要知道代码逻辑细节,执行一次,就能实时对程序已执行的每x秒的代码位置和耗时有清晰的认知。
- 工具的结果文件大小:既可以通过循环执行的间隔x来控制,也可以通过对原子化的dump结果文件进行切分和成倍稀疏筛选。
- 这对理解代码执行逻辑,加速代码开发,程序性能优化都有很大的性能提升。
备选工具¶
- VizTracer: 有点像开了profiler level0的工具
- profiling支持中断保存?
动态profiling?