像老大一样优化 Python(3)

如果内建的优化器是一门大炮,那么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代码加速很有意思,但是注意不要破坏了本身的逻辑。可读的代码比运行速度更重要。先把它缓存起来再进行优化其实更好。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/3f1795f857723ade6e76ecfe3ae2d36b.html