近期做测试和采样的一些记录
这阵子做的工作主要和性能优化有关,之前的 一篇 谈了如何获得采样数据,这里保存一些测试和采样记录。
测试程序性能#
实际中的一些压力测试和 Python 版本关系密切,这时候 six 包库是好伴侣,比如用于区分计时函数:
import six
if six.PY3:
timer_func = time.process_time
else:
timer_func = time.clock
如果可以,封装一个 timer 装饰器可以在大量测试函数中获得比较好的可视化结果,如:
import time
def timer(func):
def wrapper(*args, **kw):
start = time.process_time()
resp = func(*args, **kw)
end = time.process_time()
print(func.__name__, "\t", end - start)
return resp
return wrapper
@timer
def f(x):
return 2*x
@timer
def g(x):
return 2*x + 7
f(1)
g(3)
可以得到:
f 3.0000000000030003e-06
g 3.9999999999901226e-06
一些小结论#
- bytes IO 在 Python2 下有比 Python3 更好的性能,gist
- Python 3.7 下最新的 NumPy
速度发生了下降不是代码被改残的问题,而是已经确认正在解决的问题
- 这个问题与 Python3 的导入机制发生了变化有关,暂时没有得到具体的解释 但是是已经观察到的,并且可以通过一段简单的代码证明
import six
import time
if six.PY3:
timer_func = time.process_time
else:
timer_func = time.clock
n = 10000
start = timer_func()
for i in range(n):
timer_func()
end = timer_func()
print(end-start)
在 Python2 和 Python3
下就会发生比较显著的性能差异,而通过下面即将提到的采样技巧,则可以看到时间开销有不少在
_find_and_load_unlocked
上
采样#
之前的博文 讲了如何对 Flask 这样的应用作采样。
Chris Dent 在 Profiling WSGI Apps 一文中使用的是环境变量作为触发是否采样的方式,比较适合系统性的开发与测试。
cprofile 是我采样的主要工具(因为尝试的其他采样工具出于各种原因无法在我的工作环境正常运行),一般是在命令行调用:
python -m cProfile -o demo.prof demo.py
采样结束后需要对采样结果做分析,我常用的是:
Tuna (金枪鱼)这个词用的真多,出了上边提到的可视化工具,还有 清华大学 TUNA 协会,以及 tuna/tunasync-scripts: Custom scripts for mirror jobs
结语#
前不久 Laisky 在推特上发了很多和 Python 有关的内容,整理后的版本在其博客上 Python 之路,我注意到这么几段话:
人们在聊到语言/框架/工具性能时,考虑的是“当程序员尽可能的优化后,工具性能会成为最终的瓶颈,所以我们一定要选一个最快的”。
但事实上是,程序员本身才是性能的最大瓶颈,而工具真正体现出来的价值,是在程序员很烂时,所能提供的兜底性能。
如果你觉得自己并不是那个瓶颈,那也没必要来听我讲了
在性能优化上有两句老话:
- 一定要针对瓶颈做优化
- 过早优化是万恶之源
所以我觉得要开放、冷静地看待工具的性能。在一套完整的业务系统中,框架工具往往是耗时占比最低的那个,在扩容、缓存技术如此发达的今天,你已经很难说出工具性能不够这样的话了。
成长的空间很大,多在自己身上找原因。
我自己还不是一个精通(Mastery) Python 的程序员1。
所以我一直在怀疑,这段时间一直在工作的项目性能的下降是我的水平不够,没有注意到哪个点导致的,导致程序竟然从 CPU 密集型变成了 IO 密集型.
Dan Pink 在 Drive - The Surprising Truth About What Motivates Us 中三大职业动机中最基础一项,出自 Pin Dancing: On the “Do you want to be a programmer at fifty?” thing. ↩︎