- 人工智能
- 深度学习
- 机器学习
【免费下载链接】mxnet
Lightweight, Portable, Flexible Distributed/Mobile Deep Learning with Dynamic, Mutation-aware Dataflow Dep Scheduler; for Python, R, Julia, Scala, Go, Javascript and more
本文围绕 Apache MXNet 官方示例目录example/profiler下的性能剖析(Profiler)配套脚本展开,系统讲解如何利用mxnet.profiler模块生成 Chrome Tracing 兼容的 JSON 剖析文件,覆盖 NDArray 算子、GPU 矩阵乘法、图像数据迭代器与 Symbolic 执行器四种典型场景。读完本文,你将掌握set_config/set_state等核心 API 的完整参数语义,能复现并改造这四个示例,为自己的训练或推理代码接入剖析能力。
MXNet Profiler 是什么:从 Python API 到 C++ 引擎的剖析链路
MXNet Profiler 是内置于框架运行时的性能剖析工具,用于记录算子(Operator)执行、内存分配、C API 调用等事件的时间信息,最终以 JSON 格式输出,可直接导入 Chrome Tracing(chrome://tracing)进行时间线可视化分析。示例目录中所有脚本的产物都是这类 JSON 文件。
Python 侧的入口位于 python/mxnet/profiler.py,它通过 C API 与底层引擎交互;C API 定义在 src/c_api/c_api_profile.cc,核心的 C++ 实现则在 src/profiler/profiler.h。
从源码结构看(src/profiler/profiler.h),Profiler 的启用由两个核心概念驱动:
- ProfilerMode(剖析模式位掩码):
kSymbolic = 1(符号算子)、kImperative = 2(命令式算子)、kAPI = 4(C API 调用)、kMemory = 8(内存使用); - ProfilerState(剖析状态机):
kNotRunning = 0与kRunning = 1。
判断某类事件是否被记录的核心逻辑(src/profiler/profiler.h)是:剖析器处于kRunning状态,且当前启用的模式位与目标事件类型按位与匹配。默认模式下mode_为kSymbolic | kAPI | kMemory(src/profiler/profiler.h),即默认不剖析命令式算子。
而在 src/c_api/c_api_profile.cc 中,profile_all会被展开为对全部四种模式位取或,profile_symbolic、profile_imperative、profile_memory、profile_api则分别单独置位——这正是示例脚本中profile_all=True与profile_symbolic=True两种写法的底层含义。
核心 API 速览:set_config、set_state 与 dump
无论哪个示例,剖析流程都是固定的三步:配置(set_config)→ 启动(set_state('run'))→ 停止(set_state('stop')),必要时在异常退出前用dump()提前落盘。
set_config:剖析配置(仅接受关键字参数)
set_config(**kwargs)的完整参数如下,参数名与默认值均可从 C++ 侧ProfileConfigParam的定义(src/c_api/c_api_profile.cc)逐一对应:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
filename | str | profile.json | 剖析数据输出文件 |
gpu_memory_profile_filename_prefix | str | gpu_memory_profile | GPU 内存剖析文件名前缀(仅 CUDA 版有效) |
profile_all | bool | False | 启用全部剖析类型 |
profile_symbolic | bool | True | 是否剖析符号算子 |
profile_imperative | bool | True | 是否剖析命令式算子 |
profile_memory | bool | True | 是否剖析内存使用 |
profile_api | bool | True | 是否剖析 C API 调用 |
continuous_dump | bool | True | 是否在运行过程中周期性落盘(追加写) |
dump_period | float | 1.0 | 连续落盘的间隔秒数 |
aggregate_stats | bool | False | 是否在内存中维护聚合统计(供dumps()使用,有性能开销) |
profile_process | str | worker | 剖析worker或server;server 仅分布式 kvstore 场景可用 |
需要留意:continuous_dump默认开启意味着长任务运行过程中数据会周期性写入文件,避免崩溃丢失全部数据。若你的测试用例像 tests/python/unittest/test_profiler.py 中那样明确关闭连续落盘,则需在结束时显式调用dump()。
set_state:剖析开关
set_state(state='stop', profile_process='worker')的state仅接受'stop'或'run',在 python/mxnet/profiler.py 内部映射为整数 0/1 后下发到 C APIMXSetProcessProfilerState(src/c_api/c_api_profile.cc)。配置与启动是分离的:可以先set_config再在循环的指定迭代启动/停止,这正是精细剖析长任务的方式。
其他常用 API
dump(finished=True):立即将已收集数据写入文件;finished=False时不停止后续统计,finished=True则落盘后结束。适合程序无法正常退出前保存数据(python/mxnet/profiler.py)。dumps(reset=False, format='table', sort_by='total', ascending=False):返回聚合统计的可打印字符串,format支持'table'/'json',sort_by支持'total'/'avg'/'min'/'max'/'count'(python/mxnet/profiler.py)。需配合set_config(aggregate_stats=True)使用。pause()/resume():临时暂停/恢复剖析而不改变状态机。Domain/Task/Frame/Event/Counter/Marker:自定义剖析原语,用于在代码中手动标注感兴趣的区域或计数(python/mxnet/profiler.py)。
示例一:profiler_ndarray.py —— 剖析 NDArray 算子
运行方式(无任何前置依赖):
python profiler_ndarray.py运行后在工作目录生成profile_ndarray.json。
该脚本的主流程位于if __name__ == '__main__':块(example/profiler/profiler_ndarray.py),是"配置—启动—停止"三步法的典型示范:
mx.profiler.set_config(profile_all=True, filename='profile_ndarray.json') mx.profiler.set_state('run') # ... 执行一系列 NDArray 算子测试 ... mx.profiler.set_state('stop')profile_all=True会同时打开符号算子、命令式算子、C API 与内存四类剖析。脚本随后批量执行了十余类 NDArray 运算,其测试函数覆盖了实际工作中最常剖析的算子类别:
- 逐元素运算(
test_ndarray_elementwise):+、-、*、/、sqrt、square、norm,覆盖float32、float64、float16、uint8、int32多种数据类型,维度从 1 到 4 维随机生成; - 切片与广播(
test_ndarray_slice、test_ndarray_slice_along_axis、test_broadcast):验证切片是拷贝而非共享内存,并大量测试broadcast_to; - 归约运算(
test_reduce):sum、max、min在随机维度与keepdims组合下的行为; - 矩阵乘法(
test_dot):(3,4)×(4,5)的mx.nd.dot; - 索引与填充(
test_ndarray_choose、test_ndarray_fill、test_ndarray_onehot):choose_element_0index、fill_element_0index、onehot_encode; - 拷贝、标量运算与裁剪(
test_ndarray_copy、test_ndarray_scalar、test_clip); - 序列化(
test_ndarray_pickle、test_ndarray_saveload):pickle与mx.nd.save/mx.nd.load。
每一类测试都附带 NumPy 对照断言(如相对误差reldiff < 1e-6),因此这个脚本既是对 NDArray 功能的回归验证,又是观察各类算子耗时占比的剖析样例——剖析结果中你可以直观看到哪类运算在 CPU 上最耗时。
示例二:profiler_matmul.py —— GPU 矩阵乘法分段剖析
运行前提:安装 GPU 版 MXNet。默认命令:
python profiler_matmul.py生成profile_matmul_20iter.json。该脚本定义了四个命令行参数(example/profiler/profiler_matmul.py),便于调整剖析区间:
| 参数 | 默认值 | 含义 |
|---|---|---|
--profile_filename | profile_matmul_20iter.json | 剖析输出文件名 |
--iter_num | 100 | 总迭代次数 |
--begin_profiling_iter | 50 | 开始剖析的迭代序号 |
--end_profiling_iter | 70 | 结束剖析的迭代序号 |
典型调用:
python profiler_matmul.py --profile_filename matmul_100iter.json --iter_num 200 --begin_profiling_iter 100 --end_profiling_iter 150脚本使用符号模式(Symbolic)剖析:通过mx.sym.dot构造C = dot(A, B)的计算图,simple_bind绑定到mx.gpu(0),输入输出均为4096×4096矩阵。剖析配置为:
mx.profiler.set_config(profile_symbolic=True, filename=args.profile_filename)注意这里刻意使用profile_symbolic=True而非profile_all=True,目的是只聚焦符号算子的执行时间,避免其他剖析开销干扰矩阵乘法的测量。循环中对剖析区间的控制方式(example/profiler/profiler_matmul.py)值得借鉴:
for i in range(args.iter_num): if i == args.begin_profiling_iter: t0 = time.process_time() mx.profiler.set_state('run') if i == args.end_profiling_iter: t1 = time.process_time() mx.profiler.set_state('stop') executor.forward() c = executor.outputs[0] c.wait_to_read()通过wait_to_read()强制等待 GPU 算子完成,确保剖析到的耗时是真实执行时间而非异步排队的虚假空闲;脚本末尾还会打印duration与每算子平均毫秒数,作为快速基准参考。
示例三:profiler_imageiter.py —— 剖析图像数据迭代器
运行前需准备一个名为test.rec的 RecordIO 图像数据集文件(位于当前工作目录),创建方法见 MXNet 官方 RecordIO 数据制作教程(使用im2rec工具,其源码位于 tools/im2rec.cc 与 tools/im2rec.py)。随后运行:
python profiler_imageiter.py生成profile_imageiter.json。脚本剖析的是数据读取管线(example/profiler/profiler_imageiter.py):
data = mx.img.ImageIter(batch_size=32, data_shape=(3, 224, 224), path_imgrec=path_rec, rand_crop=True, rand_resize=True, rand_mirror=True) data.reset() tic = time.time() for i in range(n): data.next() mx.nd.waitall() print(batch_size*n/(time.time() - tic))关键点:ImageIter从test.rec读取图像,rand_crop/rand_resize/rand_mirror开启随机裁剪、缩放与翻转等在线增强,输出张量形状为(32, 3, 224, 224)。脚本在循环中不断调用data.next()拉取批次,20 个迭代后打印吞吐量(样本/秒)。
剖析配置同样为profile_all=True(example/profiler/profiler_imageiter.py)。通过剖析输出,你可以定位数据加载与预处理(解码、裁剪、镜像、类型转换、填充)在整个迭代中的耗时占比,判断训练瓶颈是否在数据侧。脚本头部注释还提示可通过os.environ["MXNET_CPU_WORKER_NTHREADS"] = "4"调整数据线程数,用于对比线程配置对吞吐的影响。
示例四:profiler_executor.py —— Symbolic 执行器剖析(README 记载)
example/profiler/README.md记载了第四个示例profiler_executor.py,用于剖析 Symbolic 执行器的执行流程。注意:当前仓库的 example/profiler 目录下并不包含该脚本(仅包含 README.md、profiler_imageiter.py、profiler_matmul.py、profiler_ndarray.py 四个文件),它属于配套的外部项目mxnet-memonger(一个内存规划辅助工具集),按 README 的指引使用步骤如下:
- 克隆
mxnet-memonger项目到本地; - 将其目录加入
PYTHONPATH:
export PYTHONPATH=$PYTHONPATH:/path/to/mxnet-memonger- 运行脚本:
python profiler_executor.py运行后将生成profile_executor_5iter.json。该示例的运行模式与前三个一致——在脚本内调用set_config/set_state包裹若干次执行器前向迭代,用于观察符号执行器在各算子上的时间分布。由于它依赖外部项目,读者可自行获取mxnet-memonger后按上述流程复现。
结果可视化:把 JSON 剖析文件变成可读的时间线
所有示例产出的 JSON 文件均采用 Chrome Tracing 格式。查看方式:
- 在 Chrome / Edge 浏览器地址栏打开
chrome://tracing; - 点击Load加载对应 JSON 文件;
- 在时间线面板中按线程/进程展开,即可看到每个算子的开始、结束时刻与耗时(火焰图式视图)。
时间线中通常能看到三类主要事件:C API 调用(MXNET_C_API域)、算子执行(符号或命令式)以及内存分配/释放事件。示例中默认生成的profile_ndarray.json、profile_matmul_20iter.json、profile_imageiter.json、profile_executor_5iter.json均可直接加载查看。
进阶技巧:内存剖析、聚合统计与分布式 server 剖析
示例脚本之外,mxnet.profiler还提供了三类值得掌握的能力:
GPU 内存剖析:set_config中的gpu_memory_profile_filename_prefix参数(仅 CUDA 构建生效,见 src/c_api/c_api_profile.cc)会生成独立的 GPU 内存剖析文件,前缀默认gpu_memory_profile,用于追踪显存分配与释放的时序。
聚合统计输出:设置aggregate_stats=True后,可用profiler.dumps()在程序内直接打印算子耗时的聚合表格(total/avg/min/max/count 排序),无需打开 Chrome Tracing 即可快速定位最耗时算子。注意 src/c_api/c_api_profile.cc 明确提示该功能有性能开销,仅在需要时开启。
分布式 server 剖析:set_config(..., profile_process='server')可将剖析命令通过 KVStore 下发到分布式训练中的 server 进程(src/c_api/c_api_profile.cc),worker 与 server 的剖析数据分开落盘。单机训练请始终使用默认的worker。相关测试可参考 tests/nightly/test_server_profiling.py。
验证与自测:仓库内的剖析测试
仓库的单元测试 tests/python/unittest/test_profiler.py 是对本文所述 API 的最佳补充验证,其中enable_profiler辅助函数(tests/python/unittest/test_profiler.py)展示了完整的参数组合写法:
profiler.set_config(profile_symbolic=True, profile_imperative=True, profile_memory=True, profile_api=True, filename=profile_filename, continuous_dump=continuous_dump, aggregate_stats=aggregate_stats) if run is True: profiler.set_state('run')其test_profiler(tests/python/unittest/test_profiler.py)与profiler_matmul.py采用了完全相同的"分段剖析"模式:先配置但不启动,循环若干次后在指定迭代set_state('run')、set_state('stop'),最后dump(True)收尾。此外该文件还覆盖了Domain、Task、Frame、Counter、Marker等自定义剖析原语的用法(tests/python/unittest/test_profiler.py),GPU 侧测试见 tests/python/gpu/test_profiler_gpu.py。在改动自己的剖析代码后,可以参照这些测试验证 API 行为是否符合预期。
小结
example/profiler目录下的四个示例覆盖了 MXNet 剖析的四大典型场景:NDArray 算子微基准(profiler_ndarray.py)、GPU 符号执行分段剖析(profiler_matmul.py)、数据管线吞吐剖析(profiler_imageiter.py)以及依赖外部项目的执行器剖析(profiler_executor.py)。它们的共同套路——set_config配置、set_state('run'/'stop')控制区间、JSON 文件导入 Chrome Tracing 可视化——可以直接迁移到任何自定义训练/推理代码中,帮助你在算子级、数据级、内存级定位性能瓶颈。
- 人工智能
- 深度学习
- 机器学习
【免费下载链接】mxnet
Lightweight, Portable, Flexible Distributed/Mobile Deep Learning with Dynamic, Mutation-aware Dataflow Dep Scheduler; for Python, R, Julia, Scala, Go, Javascript and more
相关推荐
MXNet Clojure Profiler 实战:以 profile-matmul 示例剖析算子执行性能
MXNet Clojure Profiler 实战:以 profile matmul 示例剖析算子执行性能 本文围绕 contrib/clojure packa
深度学习机器学习人工智能MXNet Profiler 实战指南:基于 example/profiler 示例的深度剖析与性能调优
MXNet Profiler 实战指南:基于 example/profiler 示例的深度剖析与性能调优 本文以 MXNet 仓库中 example/profi
深度学习机器学习人工智能arduino-esp32 异步 Wi-Fi 扫描实战:WiFiScanAsync 示例逐行解析与底层实现
arduino esp32 异步 Wi Fi 扫描实战:WiFiScanAsync 示例逐行解析与底层实现 本文以 libraries/WiFi/example
嵌入式物联网驱动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考