当前位置: 首页 > 科技观察

效率神器:快速定位最慢代码

时间:2023-03-16 18:25:35 科技观察

天下武功,唯快不破。编程也不例外。你的代码跑得很快,如果你能很快找出代码慢的原因,你的编码水平就高了。今天分享一个超实用的Python性能分析工具pyinstrument,可以快速找到代码最慢的部分,帮助提升代码的性能。支持Python3.7+,可以分析异步代码,只用一条命令展示特定代码的耗时。经常写Python的朋友一定要用。安装pipinstallpyinstrument,只需在程序开头使用,启动pyinstrument的Profiler,最后关闭Profiler,打印分析结果如下:frompyinstrumentimportProfilerprofiler=Profiler()profiler.start()#hereisthecodeyou想要分析profiler.stop()profiler。print()比如这段代码123.py,我们可以明显看出listcomprehension比较慢:frompyinstrumentimportProfilerprofiler=Profiler()profiler.start()#这里是你要分析的代码a=[iforiinrange(100000)]b=(iforiinrange(100000))profiler.stop()profiler.print()以上分析需要修改源码。如果使用命令行工具,则无需修改源代码。你只需要执行pyinstrumentxxxx.py:比如有这样一个排序程序c_sort.py:importsysimporttimeimportnumpyasnparr=np.random.randint(0,10,10)defslow_key(el):time.sleep(0.01)returnelarr=list(arr)foriinrange(10):arr.sort(key=slow_key)print(arr)这段代码中故意放了一句time.sleep(0.01)来延迟性能,看pyinstrument能不能识别,执行pyinstrumentc_sort.pyfromthecommandline:从结果来看,程序运行了1.313秒,sleep运行了1.219秒,明显是瓶颈。现在我们删除它,再看结果:删除后,性能最慢的是numpy模块的初始化代码__init__.py,不过这些代码不是自己写的,也不是特别慢,所以你不需要关心它。分析Flask代码Web应用也可以利用这个来寻找性能瓶颈,比如flask只需要记录请求前的时间,统计请求后的时间,只需要在flask的请求拦截器中写入:fromflaskimportFlask,g,make_response,requestapp=Flask(__name__)@app.before_requestdefbefore_request():if"profile"inrequest.args:g.profiler=Profiler()g.profiler.start()@app.after_requestdefafter_request(response):ifnothasattr(g,"profiler"):returnresponseg.profiler.stop()output_html=g.profiler.output_html()returnmake_response(output_html)如果有这样的API:@app.route("/dosomething")defdo_something():importrequestsrequests.get("http://google.com")返回"Googlesayshello!"为了测试这个API的瓶颈,我们可以在url中添加一个参数profile:http://127.0.0.1:5000/dosomething?profile,哪一行代码执行的比较慢,结果清晰可见:分析Django代码也很简单,只需要在Django配置文件的MIDDLEWARE中添加“pyinstrument.middleware.ProfilerMiddleware”,然后在url中添加一个参数profile即可:如果没有希望大家能看到,只有管理员可以看到,settings.py可以添加这样的代码:defcustom_show_pyinstrument(request):returnrequest.user.is_superuserPYINSTRUMENT_SHOW_CALLBACK="%s.custom_show_pyinstrument"%__name__如果不想在url后面加参数查看性能分析,可以在settings.py文件中加入:PYINSTRUMENT_PROFILE_DIR='profiles'这样,每次访问Django界面,会把分析结果以html文件的形式保存在项目目录下的profiles文件夹中分析异步代码简单的异步代码分析:importasynciofrompyinstrumentimportProfilerasyncdefmain():p=Profiler()withp:print("Hello...")awaitasyncio.sleep(1)print("...World!")p.print()asyncio.run(main())比较复杂的异步代码分析:importasyncioimporttimeimportpyinstrumentdefdo_nothing():passdefbusy_wait(duration):end_time=time.time()+durationwhiletime.time()at0x1053dpy",at0/3manage3d6a00"1>)0.0.0011.6931.693manage.py:2()10.0000.0001.5861.586__init__.py:394(execute_from_command_line)10.0000.0001.5861.586__init__.py:350(execute.1init.2py4__4__5__1fetch_command)430.0130.0001.1240.026__init__.py:1(<模块>)3880.0080.0001.0620.003re.py:226(_compile)1580.0050.0001.0480.007sre_compile.py:496(编译)10.01140.207(编译)10.01140.207__2get_commands)1530.001.010.00.001.0.00:188(编译)106/1020.0010.0001.0300.010__init__.py:52(__getattr__)10.0000.0001.0291.029__init__.py:31(_setup)10__init__.py:31(_setup)10__init__.02:57(_configure_0101.01.01.0201.01.02)20.505log.py:1()看完了,还是一头雾水。通常很难理解您自己的代码是如何与这些痕迹相关联的。Pyinstrument记录整个堆栈,因此跟踪昂贵的调用要容易得多。它还默认隐藏库框架,让您专注于影响性能的应用程序/模块:_.___/_______/_Recorded:14:53:35Samples:131/_//_////_\///_/////_'///Duration:3.131CPUtime:0.195/_/v3.0.0b3Program:examples/django_example/manage.pyrunserver--nothreading--noreload3.131manage.py:2└─3.118execute_from_command_linedjango/core/management/__init__.py:378[473frameshidden]django,socketserver,selectors,wsgi...2.836selectselectors.py:3650.126_get_responsedjango/core/handlers/base.py:96└─0.126hello_worlddjango_example/views.py:4最后,本文分享pyinstrument的用法。有了这个性能分析神器,以后在代码优化上可以节省很多时间。这样的效率神器,值得分享。毕竟,生命太短暂了。有更多时间做一些有趣的事情不是很好吗?本文转载自微信公众号“Python7号”,可通过以下二维码关注。转载本文请联系Python7号公众号。