利用 cProfile 进行 Python 程序性能调优

在上一篇文章中,我提到过 cProfile 对 Python 进行调优,但是仅仅只是简单的一笔带过,这篇文章就针对这个内容,单独扩展一下。cProfile 是 Python 的性能测试工具,另外一个同类工具是 python 实现的 profile,不过 cProfile 是 C 语言扩展的实现。

官方文档页面:http://docs.python.org/2/library/profile.html#module-cProfile

先从一个例子开始:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import time


def delay():
    time.sleep(10)


def main():
    delay()
    print "123"

if __name__ == '__main__':
    main()

使用下面这个命令就可以快速诊断:

python -m cProfile profile_run.py

输出如下:

     5 function calls in 10.002 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.002    0.002   10.002   10.002 profile_run.py:11(main)
    1    0.000    0.000   10.002   10.002 profile_run.py:4(<module>)
    1    0.000    0.000   10.000   10.000 profile_run.py:7(delay)  <-- 调用者这里
    1    0.000    0.000    0.000    0.000 {method'disable'of'_lsprof.Profiler'objects}
    1   10.000   10.000   10.000   10.000 {time.sleep}   <--- 元凶在这里

如果想输出到文件,添加一个 - o 参数即可:

python -m cProfile -o profile_output profile_run.py

若想按照某列排序,则可以添加这个参数:

python -m cProfile -o profile_output -s tottime profile_run.py

那么等等,这个结果文件如何识别呢:

ncalls: 这个函数被调用的次数

tottime:函数的具体执行时间,不包含其他函数消耗的时间

percall:每次调用的消耗(包含其他函数消耗和不包含的时间)

cumtime:程序消耗的总时间

然后看一个实际的成果看一下结果如何,具体的诊断数据:

cProfile Result

发现最慢的执行模块是 not_operation 这个函数:

def not_operation(operand, dictionary, pfile):
    """Performs the operation `NOT operand`."""

    # A list of all the documents (sorted)
    all_docs = dictionary.all_docs()

    # A list of the documents matching `operand` (sorted)
    results = get_results(operand, dictionary, pfile, force_list=True)

    return [doc for doc in all_docs if doc not in results]

接下来是怎么修复了:

# the fix.
def not_operation(operand, dictionary, pfile):
    """Performs the operation `NOT operand`."""

    # A list of all the documents (sorted)
    all_docs = dictionary.all_docs()

    # A list of the documents matching `operand` (sorted)
    results = get_results(operand, dictionary, pfile, force_list=True)

    return list_a_and_not_list_b(all_docs, results)


def list_a_and_not_list_b(a, b):
    """Returns `a AND NOT b`.

    Both a and b are expected to be sorted lists.

    """results = []
    idx_a = 0
    idx_b = 0
    while idx_a <len(a) and idx_b <len(b):
        if a[idx_a] <b[idx_b]:
            results.append(a[idx_a])
            idx_a += 1
        elif b[idx_b] <a[idx_a]:
            idx_b += 1
        else:
            idx_a += 1
            idx_b += 1

    while idx_a <len(a):
        results.append(a[idx_a])
        idx_a += 1

对了,说起来,上面图片查看工具是 cprofilev,是针对 cProfile 的查看工具,使用命令安装:

pip install bottle cprofilev

cprofilev profile_output

如果在 Windows 安装默认有点问题,你如果想解决,可以自己看一下:)

Licensed under CC BY-NC-SA 4.0
Built with Hugo
主题 StackJimmy 设计