在Python中,cProfile
是一个强大的性能分析模块,用于测量程序中各部分的执行时间。下面是一个使用cProfile
的简单示例,包括如何启动性能分析、收集数据,并查看分析报告。
你的ROS节点代码中,你可以在消息处理函数前后或整个节点的主循环中嵌入cProfile
的使用。假设你有一个ROS节点,它订阅某个话题并处理接收到的消息,你可以在处理函数开始的地方启动cProfile
,处理结束后停止并保存结果。
import rospy
from std_msgs.msg import String
import cProfile
def callback(data):
# 在这里开始性能分析
profiler = cProfile.Profile()
profiler.enable()
# 你的数据处理逻辑
process_data(data)
# 结束性能分析
profiler.disable()
profiler.dump_stats('ros_node_profile.prof') # 将结果保存到文件
def process_data(data):
# 实际的数据处理逻辑
pass
def main():
rospy.init_node('my_ros_node', anonymous=True)
rospy.Subscriber('my_topic', String, callback)
rospy.spin()
if __name__ == '__main__':
main()
1.使用pstats
模块来查看和分析这个文件的内容:
python -m pstats ros_node_profile.prof
2.使用SnakeViz进行可视化
安装SnakeViz(如果尚未安装):
pip install snakeviz
然后,运行SnakeViz来查看性能报告:
snakeviz ros_node_profile.prof
这将在你的默认浏览器中打开一个网页,展示性能分析结果的图形化视图。
通过上述步骤,你可以有效地识别出ROS节点中数据处理和传输的性能瓶颈,并据此进行优化。
可能报错:
snakeviz: error: The file /home/code/ros_node_string_prof.prof is not a valid profile. Generate profiles using:
python -m cProfile -o my_program.prof my_program.py
Note that snakeviz must be run under the same version of Python as was used to create the profile.
遇到的原因:.prof大小为0个字节,重新运行.py文件,中断即生成.prof。
太麻烦想使用注解来对函数进行修饰?下面是实例:
import cProfile
import functools
import rospy
from std_msgs.msg import String, Int32
def profile_decorator(output_file):
"""装饰器,用于为ROS回调函数添加性能分析"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
profiler = cProfile.Profile()
profiler.enable()
result = func(*args, **kwargs)
profiler.disable()
profiler.dump_stats(output_file)
return result
return wrapper
return decorator
@profile_decorator('ros_node_string_prof.prof')
def callback_string(data):
# 你的字符串数据处理逻辑
pass
@profile_decorator('ros_node_int_prof.prof')
def callback_int(data):
# 你的整型数据处理逻辑
pass
def main():
rospy.init_node('my_ros_node', anonymous=True)
rospy.Subscriber('topic_string', String, callback_string)
rospy.Subscriber('topic_int', Int32, callback_int)
rospy.spin()
if __name__ == '__main__':
main()
在这个例子中,profile_decorator
接受一个参数output_file
,用于指定性能分析结果的保存文件名。装饰器内部创建了一个新的cProfile.Profile
实例,并在被装饰的函数执行前后管理它的启停。这样,你只需在每个需要分析的回调函数定义前加上@profile_decorator('输出文件名.prof')
即可,大大减少了重复代码,使得性能分析的集成更为简洁。
问题:针对上面的情况,如果是在ros项目中,回调函数会调用多次,每次调用会覆盖之前的.prof文件。
解决:1)初始化Profiler: 在节点初始化时,你可以初始化一个全局的cProfile.Profile
实例,并在每次回调开始前重置这个实例,这样所有的调用数据都会被累加。
import cProfile
import pstats
# 初始化全局性能分析器
profiler = cProfile.Profile()
def my_callback(data):
# 回调开始前重置Profiler
profiler.enable()
# 这里是你的回调函数的正常逻辑...
# 回调结束后停止记录
profiler.disable()
# 在适当的时候(比如节点关闭时)将累积的性能数据写入文件
def write_profile_data():
profiler.dump_stats("cumulative.prof")
2)使用装饰器
编写一个装饰器来自动管理每次回调函数调用的性能分析,同时确保数据被累积到单个文件中。
from functools import wraps
import cProfile
import os
def profile_callback(func):
@wraps(func)
def wrapper(*args, **kwargs):
global profiler
if 'profiler' not in globals():
profiler = cProfile.Profile()
profiler.enable()
result = func(*args, **kwargs)
profiler.disable()
return result
return wrapper
@profile_callback
def my_callback(data):
# 回调函数的逻辑
pass
def on_shutdown(node):
global profiler
if 'profiler' in globals():
profiler.dump_stats("cumulative.prof") # 在节点关闭时保存累积的性能数据
2.使用嵌入代码记录的性能指标,只是具体到函数,每个函数的运行时间,调用次数等等。
如何具体到每一行代码的运行时间?
要查看Python程序中每行代码运行的时间,cProfile
和 line_profiler
是两个常用的工具。尽管 cProfile
提供了函数级别的性能分析,但它不直接提供逐行代码的运行时间。而 line_profiler
正是为此设计的,它可以给出每行代码的运行时间。
2.1安装:
pip install line_profiler
对标记函数进行分析:
from line_profiler import profile
@profile
def my_function():
# 你的代码...
pass
2.2 使用 kernprof
工具运行你的脚本。这个工具会生成一个 .lprof
文件,包含了每行代码的性能数据。
kernprof -l your_script.py
这里的 -l
参数表示开启行级别(line-by-line)的分析。
2.3查看分析结果
python -m line_profiler your_script.lprof