我写Python写了几年。最烦的事,就是代码跑得慢。不是一般的慢,是那种让你怀疑人生的慢。卡在那边,CPU风扇转得像直升机起飞。你盯着黑乎乎的终端,想不通哪一步耗掉了时间。
Python性能瓶颈在哪?很多人第一反应是“Python语言本身慢”。这说法没错,但太笼统。真正的瓶颈往往藏在特定函数里。可能是你写了个循环,每次从数据库拿数据。也可能是正则表达式写得有问题,匹配了几千个字符串。还有可能是第三方库的底层实现有坑。你光靠猜,永远找不到那个元凶。
我试过一个项目。处理图片,批量生成缩略图。本地跑没问题,上了服务器就卡。一开始以为是网络延迟。查了半天,发现是某个图像处理函数的参数传错了,导致每张图片都在重复做无效计算。那个函数占总时间的百分之七十。改完以后,速度直接翻了四倍。
谈一个实际好用的工具。Python内置的cProfile模块。不要装额外的包,Python自带。用法非常朴实。
写一个测试文件。比如有个函数慢得要命:
def slow_function():
total = 0
for i in range(1000000):
if i % 2 == 0:
total += i i
return total
跑一下:
python -m cProfile -s cumulative my_script.py
终端会打印一张表。看'cumtime'那一列。数字最大的那个函数,就是你等下要动手的对象。
有次我分析一个爬虫脚本。发现urllib.request.urlopen这个函数占了80%的时间。我换了requests库,再用session复用连接,时间直接砍到原来的五分之一。
cProfile输出了几十行信息。新手容易慌。别慌,记住一条:只看累计时间排前三的函数。剩下的都是杂音。你要解决的永远是那个大头。
还有个技巧。把profile结果导出成文件:
python -m cProfile -o output.prof my_script.py
然后装一个叫snakeviz的库。跑一下:
snakeviz output.prof
浏览器里会弹出一个漂亮的火焰图。哪个函数耗时多,一眼就看出来。火焰图越宽的函数,拖累代码越深。
有朋友问我:“你每次都靠这个找性能问题?”我说不是每次。有时候我也会用time模块手动计时。但cProfile是正经路子。尤其在你完全没头绪的时候,它就像个探测器。
说个我踩过的坑。有次做文本分词。用了jieba库。几万字的分词跑了快两分钟。用cProfile一查,发现词典加载函数占了92%的时间。问题不是分词本身慢,是每次调用都重新加载词典。我改成全局加载一次,任务瞬间降到了十几秒。
很多人觉得性能优化是高级玩家才做的事。其实不是。普通开发者也该掌握cProfile。你写脚本,跑批处理,做数据分析,只要代码超过一百行,迟早会遇到慢的问题。等到那时候,你再回头查bug,不如现在花十分钟跑一遍profile。
工具从来不是问题。问题是你习不习惯用。养成习惯后,你会发现自己效率高了很多。别人还在那儿拆东墙补西墙,你已经精准找到那个拖后腿的函数。改一行代码,解决问题。