性能分析 - Ligcox/BTP_DM GitHub Wiki
BTPDM是一个基于Python开发的框架,Python像Java/C#一样,也是一种根据虚拟机的语言。Python程序运行时,编译的结果被保存在内存中的PyCodeObject中,当Python程序运行结束时,Python解释器将PyCodeObject写回pyc文件中。Python作为一门脚本语言相对于编译型语言有以下显著的优势: 无需编译:在目标平台只有要解释器即可运行 开发效率高:Python是一门语法极为简洁的语言,能够显著缩短程序的开发时间。 生态丰富:在Pypi上能够找到全球开发者针对不同场景开发的库
但Python与C/C++相比也有显著的缺点:执行效率低下。因此,我们在这一章对BTPDM的运行进行一定的分析。
在/src/Sample/c2py/展示了使用pybind11进行Python和C++混合编程的例子
既然解释型语言拥有较高的执行速度,为什么不使用C/C++进行较为简单的模块处理,然后在再由Python进行调用,这样是否能够提升BTPDM的整体运行效率。
事实上,大量的Python第三方模块均采用了C/C++和Python进行混合编程,例如NumPy、OpenCV等。通过Python接口调用这些模块时,底层仍使用的C++进行运算的。
我们在test_funcs.py
展示了一个利用Python调用C++模块进行简单的例子。通过获取CPU级别精度的时间并对运行时间进行记录。结果发现,仅使用C++进行简单的数值运算(在样例中展示的是计算的两个数字之和)并返回Python使用与直接使用Python编程相比,纯Python的运行效率是C++混合编程的70倍!在C++中执行进行重复数值运算直接返回Python结果,C++展示出其性能的优越,和纯Python编程相比,C++混合编程的程序效率会高出10倍。
在调用函数的过程中,混合编程的大量时间不可避免地用于堆栈指针进行值传递,简单的运算纯Python编程却能够直接规避这一问题。而第三方库已经提供了各个平台下编译完成的.pyd
文件(例如NumPy和OpenCV),调用这些库的过实际上已经不是使用Python实现进而提高了程序的运行效率。
从开发效率上来看,在混合编程的过程中,需要额外地编写类似setup.py
和wrap.cpp
的wrap文件,这严重拖慢整个BTPDM的开发周期。另外,BTPDM主要为了快速适应RoboMaster赛场环境上的跨平台框架,使用混合编程的却依旧需要对混合编程部分进行编译,这无疑与BTPDM的开发初衷有所冲突。
综合来看,在较为复杂的运算使用C/C++进行混合编程能够对程序整体的运行效率有较大地提升,第三方库在这一点上已经做的较为完善。综合考虑开发周期、跨平台移植等一系列原因,在BTPDM的主体开发依然是使用Python进行的。此外,随着Python解释器具体实现的发展,类似PyPy解释器已经逐渐成熟,使得Python的运行效率能够接近C/C++。我们有理由相信(事实上我们接下来也会看到)使用Python作为主体开发语言的TPDM能够较好的适应RoboMaster赛场。
在/src/Sample/performanceAnalysis/展示了使用BTPDM进行性能分析的例子
在/src/Sample/performanceAnalysis/也保存了性能分析的结果源文件,可以使用snakeviz等工具进行进一步的分析
Python提供了Profilers 分析器cProfile和profile。profile 是一组统计数据,描述程序的各个部分执行的频率和时间。这些统计数据可以通过pstats
模块格式化为报表。对于BTPDM的性能分析,我们使用了cProfile
;这是一个C扩展插件,因为其合理的运行开销,所以适合于分析长时间运行的程序。该插件基于lsprof
,由Brett Rosen和Ted Chaotter编写。
本章节中的性能测试将BTPDM在debug和非debug模式下分别运行一段时间(近似于main.py
的运行时间)。程序运行环境为:
- 开发板NVIDIA Jetson AGX
- 系统版本Ubuntu 18.04.5 LTS
- 功率模式MAXN
下面的表格分别展示了debug模式下和关闭debug模式后BTPDM运行一段时间,按照调用时间降序排列的前20条数据和Icicle风格的程序分析图。
debug模式
ncalls | tottime | percall | cumtime | percall | function |
---|---|---|---|---|---|
164/1 | 0.012 | 0.000 | 384.682 | 384.682 | {built-in method builtins.exec} |
1 | 0.000 | 0.000 | 384.682 | 384.682 | main.py:10() |
1 | 0.131 | 0.131 | 383.566 | 383.566 | scheduler.py:234(run) |
24448 | 0.303 | 0.000 | 383.436 | 0.016 | BCPloger.py:75(wrapper) |
24448 | 0.921 | 0.000 | 383.092 | 0.016 | scheduler.py:238(main_task) |
24448 | 1.439 | 0.000 | 322.879 | 0.013 | scheduler.py:186(task_auto_aiming) |
24448 | 0.717 | 0.000 | 242.548 | 0.010 | targetDetector.py:221(process) |
48895 | 166.692 | 0.003 | 166.692 | 0.003 | {waitKey} |
24448 | 0.690 | 0.000 | 149.993 | 0.006 | targetDetector.py:227(updateProcess) |
24448 | 5.585 | 0.000 | 69.286 | 0.003 | imageFilter.py:20(process) |
24448 | 1.778 | 0.000 | 62.685 | 0.003 | targetDetector.py:83(detectArmor) |
24438 | 2.768 | 0.000 | 53.912 | 0.002 | targetDetector.py:133(pnp_info) |
24003 | 32.643 | 0.001 | 32.643 | 0.001 | {solvePnP} |
24448 | 2.464 | 0.000 | 29.113 | 0.001 | targetDetector.py:35(detectLightStrip) |
171136 | 23.855 | 0.000 | 23.855 | 0.000 | {threshold} |
24447 | 1.922 | 0.000 | 22.620 | 0.001 | BCPloger.py:118(dispaly_loger) |
24448 | 0.860 | 0.000 | 18.271 | 0.001 | imageFilter.py:64(updateProcess) |
1470633 | 17.553 | 0.000 | 17.553 | 0.000 | {method 'write' of '_io.TextIOWrapper' objects} |
73344 | 0.406 | 0.000 | 13.344 | 0.000 | utils.py:160(debug_show_window) |
48896 | 13.011 | 0.000 | 13.011 | 0.000 | {cvtColor} |
73344 | 12.937 | 0.000 | 12.937 | 0.000 | {imshow} |
48896 | 12.035 | 0.000 | 12.035 | 0.000 | {blur} |
关闭debug模式
ncalls | tottime | percall | cumtime | percall | function |
---|---|---|---|---|---|
164/1 | 0.010 | 0.000 | 480.604 | 480.604 | {built-in method builtins.exec} |
1 | 0.000 | 0.000 | 480.604 | 480.604 | main.py:10() |
1 | 0.182 | 0.182 | 479.652 | 479.652 | scheduler.py:234(run) |
45211 | 0.449 | 0.000 | 479.470 | 0.011 | BCPloger.py:75(wrapper) |
45211 | 13.544 | 0.000 | 478.964 | 0.011 | scheduler.py:238(main_task) |
33882 | 1.425 | 0.000 | 389.672 | 0.012 | scheduler.py:186(task_auto_aiming) |
33882 | 7.307 | 0.000 | 197.204 | 0.006 | imageFilter.py:20(process) |
33882 | 0.733 | 0.000 | 177.910 | 0.005 | targetDetector.py:221(process) |
33882 | 1.527 | 0.000 | 79.050 | 0.002 | imageFilter.py:64(updateProcess) |
67762 | 78.735 | 0.001 | 78.735 | 0.001 | {waitKey} |
33881 | 2.490 | 0.000 | 77.507 | 0.002 | targetDetector.py:83(detectArmor) |
33880 | 3.792 | 0.000 | 65.016 | 0.002 | targetDetector.py:133(pnp_info) |
237173 | 58.930 | 0.000 | 58.930 | 0.000 | {threshold} |
33881 | 0.862 | 0.000 | 56.216 | 0.002 | targetDetector.py:227(updateProcess) |
203052 | 52.012 | 0.000 | 52.012 | 0.000 | {built-in method numpy.core.multiarray.concatenate} |
33882 | 3.538 | 0.000 | 43.430 | 0.001 | targetDetector.py:35(detectLightStrip) |
237174 | 36.958 | 0.000 | 36.958 | 0.000 | {bitwise_and} |
33763 | 36.318 | 0.001 | 36.318 | 0.001 | {solvePnP} |
33881 | 2.660 | 0.000 | 35.134 | 0.001 | BCPloger.py:118(dispaly_loger) |
2062742 | 27.884 | 0.000 | 27.884 | 0.000 | {method 'write' of '_io.TextIOWrapper' objects} |
具体使用过程中,可以在
/src/main.py
中切换DEBUG_MODEL
的值开启和关闭debug模式
如:gvm.set_value("DEBUG_MODEL", "DEBUG_SETTING", False)
表示关闭debug模式
BTPDM在测试环境下能够有效地对装甲板目标进行识别和侦测。在非debug模式下运行图像主处理任务平均调用时间为0.0115s,日志等非核心操作对程序性能影响为7.31%。性能主要花费在图像二值化、pnp位姿解算等方面。
debug模式
关闭debug模式
可以看到,在debug模式下,一部分时间被花费在了cv2.imshow
函数等debug刷新上,这部分函数对性能的影响超过17%!关闭debug后程序整体运行效率提升10.9567%,主要功能函数运行效率提升了12.9169%。BTPDM的BCP部分对1.0723%。