
# 人工智能性能优化
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资源池集群。详细步骤可参考[最佳实践文档](https://support.huaweicloud.com/resmgmt-modelarts/umn-cluster-modelarts-0015.html)。
   
3. **训练显存优化实践**
   - **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带宽，有一定的时延，可能会降低吞吐。
     
   
   - **观测性能指标**
     表1观测性能指标 
     | 指标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支持的监控指标](https://support.huaweicloud.com/usermanual-standard-modelarts/modelarts_23_0186.html)文档。
     
    
 
