问题描述

在昇腾A+X 16卡上用Pytorch Lighting训练多模态语音模型Make-An-Audio-2时遇到报错:rtEnableP2P failed。

问题定位和解决过程

阶段1

1)通过log定位到报错的位置是在to方法内部,传入一个model,实际上挨个将每个tensor转移到目标device上。
2)按照调用堆栈向上排查,发现正在将模型权重move_to_device,通过打印发现部分权重在cpu,部分在npu:0上。
3)权重原本是在cuda上,通过transfer_to_npu加载了到了npu:0上。
4)理论上应该加载到各自的device,但此时先想到应该和其它一样加载到cpu上,因此先把这部分权重放到了cpu。
5)问题解决,但在下一步又出现了同样报错。

阶段2

1)继续按照调用堆栈向上排查,发现是在forward方法中,计算用的算子权重在npu:0上。
2)只有set_device()后才会迁移到各自的卡上,此时以为模型代码和pytorch_lighting没有set_device。
3)打开代码发现pytorch_lighting中有set_device,但在初始化模型后面。
4)排查模型代码,发现模型初始化有很多.cuda操作,通过transfer_to_npu加载了到了npu:0上。
5)删除这些.cuda操作,在forward时再to npu即可。

问题根因分析

底层原理

每张卡pcie有16个atu资源,每张卡通信时都占用2个atu(一个发送,一个接受),意味着同时最多只能和8个device p2p enable.组网时HCCL又限制了只能在当前环所有卡,和另外一个环里的一张卡,比如0和8,1和9。也可以0和15,但需要设置。

直接原因

A+X 16卡实际分为前8卡和后8卡两个环,NPU不支持前8卡和后7卡通信。而模型中存在0卡tensor往后7卡的p2p拷贝动作,因此报错。之所以存在0卡tensor往后7卡拷贝有两个原因:
原因一:

  1. 模型加载的部分CKPT默认在NPU上
  2. 模型部分结构在初始化时,加了.cuda()操作,transfer_to_npu()会给他转成.npu()。

原因二:
模型使用pytorch_lighting框架中的训练方法,导致逻辑顺序是这样的:

  1. 模型初始化各网络层
  2. 模型加载ckpt,并move到device上
  3. 在pytorch_lighting框架中初始化分布式训练,为16张卡分别绑定独立进程
  4. 给每个进程set_device,绑定device
  5. 开始训练

重点:只有在set_device后,.npu() 或.cuda()等操作才会是拷贝到各个卡上,而在set_device前,只会拷贝到0卡。因此,模型初始化各网络层时的npu()数据 和 模型加载的ckpt都在0卡上,但训练时产生的数据都在各个卡上,一起计算时就会出现0卡和其它卡之间的通信拷贝。

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐