更新时间:2024-08-30 GMT+08:00
分享

人工智能性能优化

1.训练优化模型性能提升实践

参数调优策略:调整模型flash attention、并行切分策略、micro batch size、重计算策略等参数。

尽可能充分利用显存和算力,通过参数调优,初步优化性能。

性能拆解

参数调优后性能仍然与转商目标有较大的差距,需要考虑进行profiling,采集性能数据后从更底层的算子、通信、调度和内存等维度将性能进行拆解分析,训练脚本中加入profiling代码。具体步骤:生成profiling数据目录结构;利用att工具,将NPU与竞品之间的数据进行端到端耗时对比分析;Tracing分析。

算子分析

通过生成profiling中的summary文件对具体的算子进行分析,考虑算子层面向FA与MM算子方向优化。

2.路由规划加速最佳实践

ranktable路由规划是一种用于分布式并行训练中的通信优化能力,在使用NPU的场景下,支持对节点之间的通信路径根据交换机实际topo做网络路由亲和规划,进而提升节点之间的通信速度。本案例介绍如何在ModelArts Lite场景下使用ranktable路由规划完成Pytorch NPU分布式训练任务,训练任务默认使用Volcano job形式下发到Lite资源池集群。详细步骤可参考最佳实践文档

训练显存优化实践

pytorch的内存池基本管理策略

  • pytorch的内存池以block为粒度来进行管理,block池分为小内存池与大内存池,block是pytorch向device驱动申请内存的粒度,整存整取。用户/Pytorch代码向内存池申请内存的接口归一为tensor的申请释放(这点最开始也不是很好理解,也就是说:任何一个pytorch代码申请内存的地方,均表现为一个tensor的申请释放)。
  • tensor的生命周期使用类似智能指针的引用计数方式来管理,且打通了Python与C++的通道,即:一个Python的tensor对象关联一个C++的tensor对象,Python的tensor对象的消失会触发C++的tensor对象析构释放内存。一个在C++环境里创建的tensor对象可以返回成一个Python的tensor对象。
  • C++的tensor对象分为两部分:一部分是viewTensor,包含tensor的各种meta信息:shape, stride, dataType等,一部分是storageTensor,包含具体的内存addr,offset,对外呈现的是viewTensor,这是pytorch做view类操作后,多个tensor对应同一块内存的基本支撑。在storageTensor申请时,向pytorch的block池申请一块内存,找到空闲块之后,视实际要求对block进行切分使用并返回address指针。

pytorch的内存的跨流复用策略

  • 如果一个stream上的内存池里申请的tensor需要给另一个stream使用,那么则需要进行recordStream操作,将这个tensor的所属block标识上新stream的信息,在这个tensor的生命周期消失触发address释放时,发现其所属block有其他stream信息,此时会给对应stream下发一个event_record task然后返回。
  • 在之后本stream在每一次新申请内存时,均做一下event_query操作,如果发现event已经被record则其他stream上的task已经执行完成,此时可以放心地回收这个block。

Pytorch的内存统计信息说明

  • pytorch的内存一般看三个峰值信息:allocated / active / reserved。allocated对应host上的tensor实际申请了但是没释放的内存(注意:是在host上申请释放,不代表device状态)。active对应host上还未释放的内存+还在被别的流占用的内存。
  • 举例,一个tensor在streamA上申请了,让供streamB做allreduce的集合通信操作,然后tensor进过一次add后被释放,此时:释放会减去allocated值,但是不会减去active 值,直至这个allreduce真实执行之后,通过query_event查询到结果之后触发释放,才会减去active值。
  • reserved对应pytorch向device申请了的内存,比如申请了100M,然后释放了,然后又申请了20M,还未释放,此时的allocated 为20M,reserved为100M。真实网络里,reserved里存在大量block被切小了的可使用内存但是当申请一个大块内存时又无法复用,这种会导致reserved 与 allocated 的较大差值,通常称为内存碎片。

pytorch的内存碎片影响因素

  • 一个step里更多的内存申请释放内存次数理论上一定会导致更多的内存碎片,为什么说是一个step呢?因为pytorch内存池只取决于host上的训练脚本逻辑,而每个step的训练脚本逻辑是相同的,所以一般第一个step之后内存状态可以稳定下来。
  • 不同生命周期的tensor交替地申请释放,因为pytorch向驱动申请是整存整取,所以:一个常规的优秀做法是把长生命周期放在最开始申请,这样不易形成碎片。而workspace内存由于可以绝对意义上地串行复用,因此对此单独做一个定制的内存池策略可以减少对内存碎片影响,在NPU上常见的非连续转连续操作,就是一个相对GPU来说较多的内存申请。

显存优化策略

由于大模型的参数成倍数的增长,远超出了单GPU物理显存所能承载的范围,大模型训练必然需要进行显存优化。显存优化要么是优化算法本身,降低模型算法的显存消耗;要么是去扩大显存,通过一些置换方式获得“额外“空间,由于显存物理大小一定,我们获得额外空间的方式不外乎两种:时间换空间和空间转移。其中,时间换空间通常会消耗算力、带宽;空间转移主要是消耗I/O带宽,有一定的时延,可能会降低吞吐。

观测性能指标

指标ID

指标名称

指标说明

cpu_usage

CPU使用率

该指标用于统计ModelArts用户服务的CPU使用率。

mem_usage

内存使用率

该指标用于统计ModelArts用户服务的内存使用率。

gpu_util

GPU使用率

该指标用于统计ModelArts用户服务的GPU使用情况。

gpu_mem_usage

GPU显存使用率

该指标用于统计ModelArts用户服务的GPU显存使用情况。

npu_util

NPU使用率

该指标用于统计ModelArts用户服务的NPU使用情况。

npu_mem_usage

NPU显存使用率

该指标用于统计ModelArts用户服务的NPU显存使用情况。

disk_read_rate

磁盘读取速率

统计ModelArts用户服务的磁盘读取速率。

disk_write_rate

磁盘写入速率

统计ModelArts用户服务的磁盘写入速率。

全量指标可参考ModelArts支持的监控指标文档。

相关文档