如果内建的优化器是一门大炮,那么line profiler可以看作是一门离子加农炮。它非常的重量级和强大。
在这个例子里,我们会用非常棒的line_profiler库。为了容易使用,我们会再次用装饰器包装一下,这种简单的方法也可以防止把它放在生产代码里。
try:
from line_profiler import LineProfiler
def do_profile(follow=[]):
def inner(func):
def profiled_func(*args, **kwargs):
try:
profiler = LineProfiler()
profiler.add_function(func)
for f in follow:
profiler.add_function(f)
profiler.enable_by_count()
return func(*args, **kwargs)
finally:
profiler.print_stats()
return profiled_func
return inner
except ImportError:
def do_profile(follow=[]):
"Helpful if you accidentally leave in production!"
def inner(func):
def nothing(*args, **kwargs):
return func(*args, **kwargs)
return nothing
return inner
def get_number():
for x in xrange(5000000):
yield x
@do_profile(follow=[get_number])
def expensive_function():
for x in get_number():
i = x ^ x ^ x
return 'some result!'
result = expensive_function()
如果你运行上面的代码,你就可以看到一下的报告:
Timer unit: 1e-06 s
File: test.py
Function: get_number at line 43
Total time: 4.44195 s
Line # Hits Time Per Hit % Time Line Contents
==============================================================
43 def get_number():
44 5000001 2223313 0.4 50.1 for x in xrange(5000000):
45 5000000 2218638 0.4 49.9 yield x
File: test.py
Function: expensive_function at line 47
Total time: 16.828 s
Line # Hits Time Per Hit % Time Line Contents
==============================================================
47 def expensive_function():
48 5000001 14090530 2.8 83.7 for x in get_number():
49 5000000 2737480 0.5 16.3 i = x ^ x ^ x
50 1 0 0.0 0.0 return 'some result!'
你可以看到,有一个非常详细的报告,能让你完全洞悉代码运行的情况。不想内建的cProfiler,它能计算话在语言核心特性的时间,比如循环和导入并且给出在不同的行花费的时间。
这些细节能让我们更容易理解函数内部。如果你在研究某个第三方库,你可以直接将其导入并加上装饰器来分析它。
一些小技巧:只装饰你的测试函数并将问题函数作为接下来的参数。
Line Profiler 优点:有非常直接和详细的报告。能够追踪第三方库里的函数。
Line Profiler 缺点:因为它会让代码比真正运行时慢很多,所以不要用它来做基准测试。这是额外的需求。
总结和最佳实践
你应该用更简单的工具来对测试用例进行根本的检查,并且用更慢但能显示更多细节的line_profiler来深入到函数内部。
九成情况下,你可能会发现在一个函数里循环调用或一个错误的数据结构消耗了90%的时间。一些调整工具是非常适合你的。
如果你仍然觉得这太慢,而是用一些你自己的秘密武器,如比较属性访问技术或调整平衡检查技术。你也可以用如下的方法:
1.忍受缓慢或者缓存它们
2.重新思考整个实现
3.更多使用优化的数据结构
4.写一个C扩展
注意了,优化代码是种罪恶的快感!用合适的方法来为你的Python代码加速很有意思,但是注意不要破坏了本身的逻辑。可读的代码比运行速度更重要。先把它缓存起来再进行优化其实更好。