MindInsight主要功能介绍
MindInsight详细功能介绍请参考MindInsight性能调试。
MindInsight主页面包括主要功能的概览信息:迭代轨迹、算子耗时统计、数据准备、时间线、调优助手。
迭代轨迹:展示了每个迭代各个阶段的性能信息:包括迭代间隙、前向反向、迭代拖尾以及每个all_reduce的信息。下方展示各个阶段的变化趋势。
算子耗时统计:通过类型、详细信息两个维度展示AICORE和AICPU算子耗时统计信息。
数据准备:性能分析分为两部分:1、迭代间隙数据处理分析;2、数据处理pipeline分析。
迭代间隙阶段:下图展示了迭代间隙阶段执行的操作的流程,通过分析队列中数据的情况判断出现性能问题的步骤。
数据处理阶段:分析用于已经定位到了该阶段的问题时,定位其中哪个算子存在问题,通过各个算子之间的队列的使用率,判断前面的算子能否提供足够的数据到队列中供下一个算子使用。
时间线:展示了算子在各个stream上的起止时刻,执行顺序、算子间隙、allreduce信息,从详细的粒度展示算子执行情况。
用户可参考下图,进行算子分析(常用的为MindData阶段分析和算子性能分析)。
案例问题分析
问题1:迭代间隙过长
通过迭代轨迹发现,迭代间隙过长:
问题分析
1.迭代间隙过长,通常因为数据处理过程导致,进入数据处理阶段分析。
2.主机队列几乎为空,判定是数据处理算子问题,进入数据处理pipeline查看具体问题。
3.当算子左边连接的Queue使用率都比较高,右边连接的Queue使用率比较低时,该算子可能是性能瓶颈:图中红框内数据可以看出,map操作过程中的队列使用率高,而右边连接的队列使用率较低,判断map中的数据处理过程存在性能瓶颈。
4.分析map中数据处理相关代码:发现数据处理进程数为默认值1,可以尝试调整数据处理进程数。
5.分析map中数据处理相关代码:发现存在c_transform和py_transform混用的问题,降低了训练性能。
措施1:调整数据处理进程数
调整数据处理进程数为8:
if do_train:
cifar_ds = ds.Cifar10Dataset(dataset_dir=data_home, num_parallel_workers=8, shuffle=True, usage='train')
else:
cifar_ds = ds.Cifar10Dataset(dataset_dir=data_home, num_parallel_workers=8, shuffle=False, usage='test')
cifar_ds = cifar_ds.map(operations=transform_label, num_parallel_workers=8, python_multiprocessing=True, input_columns="label")
cifar_ds = cifar_ds.map(operations=transform_data, num_parallel_workers=8, python_multiprocessing=True, python_multiprocessing=True, input_columns="image")
cifar_ds = cifar_ds.batch(batch_size, num_parallel_workers=8, drop_remainder=True)
修改后,重新训练,将训练后代码继续做profiling,发现迭代间隙明显缩短。
性能对比: 改进前:1100imgs/sec 改进后: 2150imgs/sec
措施2:避免c_transform和py_transform混用
数据处理过程中发现存在c_transform和py_transform混用的情况:
if do_train:
# Transformation on train data
transform_data = py_trans.Compose([
CV.RandomCrop((32, 32), (4, 4, 4, 4)),
py_vision.ToPIL(),
py_vision.RandomHorizontalFlip(),
CV.Rescale(rescale, shift),
CV.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
CV.HWC2CHW()
])
else:
# Transformation on validation data
transform_data = py_trans.Compose([
CV.Rescale(rescale, shift),
CV.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
CV.HWC2CHW()
])
cifar_ds = cifar_ds.map(operations=transform_data, input_columns="image")
将数据处理过程中代码进行如下修改:
if do_train:
# Transformation on train data
transform_data = C.Compose([
CV.RandomCrop((32, 32), (4, 4, 4, 4)),
CV.RandomHorizontalFlip(),
CV.Rescale(rescale, shift),
CV.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
CV.HWC2CHW()])
else:
# Transformation on validation data
transform_data = C.Compose([
CV.Rescale(rescale, shift),
CV.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
CV.HWC2CHW()])
cifar_ds = cifar_ds.map(operations=transform_data, input_columns="image")
修改后,重新训练,将训练后代码继续做profiling。迭代间隙有所缩短。
性能对比: 改进前:2150imgs/sec 改进后: 2250imgs/sec
经过这两步优化,迭代间隙由原来的77.5027ms减少到17.0623ms,有明显改善。
问题2:数据队列为空
继续分析优化后的Profiler数据,发现数据准备中的主机队列几乎为空:
问题分析
- 进入数据处理pipeline查看具体问题。
2.如红框所示,数据处理过程中的队列使用率高。对于最右侧的算子,如果其左边所有Queue的使用率都比较高,该算子可能是性能瓶颈。而此处最右侧算子为框架自动插入算子,因此判断数据下发存在性能瓶颈,应提升数据从Host传输到Device的速度。
措施3:采用数据下沉模式
采用数据下沉模式,实现整图下沉到Device执行,避免Host-Device频繁交互,减小了数据传输开销。
将Model.train接口中dataset_sink_mode值设为True,即可采用数据下沉模式。
model.train(..., dataset_sink_mode=True, sink_size=steps_per_epoch_train)
修改后,重新训练,将训练后代码继续做profiling,发现主机队列为空比例大大下降。
数据处理过程中的队列使用率明显降低。
性能对比: 改进前:2250imgs/sec 改进后: 2350imgs/sec
问题3:前向+反向时间较长
继续分析优化后的Profiler数据,由迭代轨迹看出,前反向时间相对较长,可能存在优化空间:
措施4:使用混合精度
使用混合精度可以加速训练,减少前反向时间。
修改高阶API代码中的Model接口,将amp_level设置成"O3",网络将采用FP16进行训练。
net = Model(net, loss, opt, metrics=metrics, amp_level="O3")
修改后,重新训练,将训练后代码继续做profiling。发现前反向时间明显减少。
性能对比: 改进前:2350imgs/sec 改进后: 3500imgs/sec
经过以上优化,训练性能得到明显提升。由初始的1100imgs/sec,改进到3500imgs/sec。