天下武功,唯快不破。编程也不例外。你的代码跑得很快,如果你能很快找出代码慢的原因,你的编码水平就高了。今天分享一个超实用的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()
