问题描述
训练条件:
Linux Euler OS x86; 8 显卡; 物理 CPU 2; 每个物理 CPU 中的核数 26; 逻辑 CPU 104; MindSpore 1.2.0 TensorFlow 1.15.0
GPU 训练时长
MindSpore: 1:58 TensorFlow: 1:04
优化目的
在相同条件下,使用 MindSpore 框架训练网络的时长 小于或等于 使用 TensorFlow 框架训练的时长
问题分析
- 该网络为了防止过拟合,会基于同一模型同时训练多个神经网络。通过阅读代码,发现无论是 CPU 训练还是 GPU 训练,在训练多个神经网络时,会给每一个神经网络绑定一个逻辑 CPU 进行训练。
- 监控 GPU 训练过程,发现训练时,被绑定的逻辑 CPU 资源占用率一直为100%,据此推测是网络训练过程中的一些与 CPU 有关的操作耗资源太大,导致训练总时长增加。
- 关闭代码中,每个网络绑定一个逻辑 CPU 进行训练的逻辑。再监控 GPU 训练过程,发现训练时,逻辑 CPU 资源占用率为250%左右。
- 对比 TensorFlow,保留/注释每个网络绑定一个逻辑 CPU 进行训练的部分代码,监控 GPU 训练过程,发现绑定一个逻辑 CPU 时,被绑定的逻辑 CPU 资源占用率为100%;而当不绑定逻辑 CPU 时,逻辑 CPU 资源占用率为150%左右。
--
5. 对比 TensorFlow,MindSpore 框架训练该网络所需要的 CPU 资源更多。据此可以断定,使用 MindSpore 框架训练该网络的性能瓶颈在 CPU 相关的操作部分。 6. 由于在 GPU 上训练,CPU 相关的操作只涉及样本数据的处理、数据处理完后拷贝到 GPU 这些。咨询 MindSpore 框架负责数据处理部分的专家,了解到原因为:
1. 涉及 CPU 的主要操作为,在每一个 step,一个 batch 的数据输入一层网络,计算完成后,都需要做一次类型转换,才能够输入到下一层网络。所以这里存在
把数据从 GPU 拷贝至 CPU -> 数据类型转换 -> 把数据从 CPU 拷贝回 GPU
这三步(不一定准确,具体需要再看代码实现)。
2. 数据处理(数据同步和拷贝),用了 MindDataset 和 Batch 两个模块。在不额外设置线程数的情况下,默认会给每个模块分别分配2个线程(主线程做简单的同步,子线程并行地进行数据拷贝)。另外再加上一个 ctrl+c 线程,总共会分配5个线程。
3. 根据一个逻辑 CPU 处理两个线程的基本原则,不绑定逻辑 CPU 的情况下,5个线程就会占用约2.5个逻辑 CPU(这与之前的测试数据相吻合)。而在绑定逻辑 CPU 的情况下,单个逻辑 CPU 启用5个线程处理数据,会导致线程竞争,这可能是造成训练耗时比较长的主要原因。
优化方案
弃用 MindDataset 和 Batch 两个模块,改用 GeneratorDataset 模块(2个线程)对数据进行预处理以及负责数据的拷贝,这样从
线程数:2(MindDataset) + 2(Batch) + 1(ctrl + c) = 5 CPU数:5 / 2 = 2.5
降为
线程数:2(GeneratorDataset) + 1(ctrl + c)= 3 CPU数:3 / 2 = 1.5
减少了所需要的 CPU 数,减少了线程数以降低线程竞争现象。最终达到的效果为,进行 GPU 训练时,GPU 训练性能基本与 TensorFlow 持平:
MindSpore: 0:57 TensorFlow: 1:04
反思
同样是数据同步和拷贝,GeneratorDataset 模块只需要2个线程, 而MindDataset 和 Batch 两个模块需要4个线程。也就是说,2个线程可以做完的事情,分配了4个线程去做。这是不是 CPU 资源分配的不合理呢?
在本次网络训练中,数据样本量较小(7k行,3MB)。但尽管如此,在数据处理过程中,由于 Batch Size = N 是预先设置好的(超参数),每一个 step 中固定读取 N 条样本数据。实践证明,训练该网络,每一个 step 处理的这 N 条样本数据,3个线程也就是1.5个左右的逻辑 CPU 资源就够了。MindDataset 和 Batch 两个模块默认分配至少4个线程处理数据的设计,是值得讨论的。至少在这个网络对应的应用场景下,这两个模块的设计是可以优化的。
其实,单看数据处理(数据同步和拷贝)这部分,GeneratorDataset 模块也需要2个线程,加上 ctrl + c 的1个线程,总共占用了3个线程也就是1.5个 CPU 资源,这是否合理呢?
在不绑定逻辑 CPU 的情况下验证一下。发现使用MindDataset 和 Batch 两个模块时,虽然逻辑 CPU 占用率为250%左右,但是每个 CPU 的用户空间使用率并不高。进一步证实了 CPU 资源分配的不合理
如果是在 GPU 上进行训练,那么网络的计算在 GPU 上,数据处理在 CPU 上,没有问题。但是如果在 CPU 上进行训练,那么网络计算所需要的资源也需要 CPU 提供。光是数据处理就占了1个逻辑 CPU,整个网络的训练性能可以达到什么程度,很难想象。