>백엔드 개발 >파이썬 튜토리얼 >Python 성능 분석 도구 소개

Python 성능 분석 도구 소개

高洛峰
高洛峰원래의
2016-11-18 13:37:271457검색

성능 분석 및 튜닝 도구 소개

프로그램 실행의 효율성을 높이고 싶고, 어떤 부분이 너무 오래 걸려 병목 현상이 발생하는지, 알고 싶을 때가 항상 있을 것입니다. 프로그램이 실행 중일 때 메모리 및 CPU 사용량. 이때 성능 분석 및 프로그램 조정을 수행하려면 몇 가지 방법이 필요합니다.

컨텍스트 관리자에 의해

컨텍스트 관리자 자체에 의해 타이머를 구현할 수 있습니다. 이전 timeit 소개 기사에서 수행한 작업을 확인하고 __enter__ 및 __exit__ 메서드를 정의하여 관리 기능을 구현할 수 있습니다. 타이밍, 유사:

# timer.py
import time

class Timer(object):
    def __init__(self, verbose=False):
        self.verbose = verbose

    def __enter__(self):
        self.start = time.time()
        return self

    def __exit__(self, *args):
        self.end = time.time()
        self.secs = self.end - self.start
        self.msecs = self.secs * 1000            # 毫秒
        if self.verbose:
            print 'elapsed time: %f ms' % self.msecs

다음과 같이 사용:

from timer import Timer

with Timer() as t:
    foo()
print "=> foo() spends %s s" % t.secs

By Decorator

그러나 내 생각에는 데코레이터 방법이 더 우아합니다

import time
from functools import wraps

def timer(function):
    @wraps(function)
    def function_timer(*args, **kwargs):
        t0 = time.time()
        result = function(*args, **kwargs)
        t1 = time.time()
        print ("Total time running %s: %s seconds" %
                (function.func_name, str(t1-t0))
                )
        return result
    return function_timer

사용 방법은 매우 간단합니다.

@timer
def my_sum(n):
    return sum([i for i in range(n)])

if __name__ == "__main__":
    my_sum(10000000)

실행 결과:

➜  python profile.py
Total time running my_sum: 0.817697048187 seconds

시스템 자체 시간 명령

사용 예는 다음과 같습니다.

➜ time python profile.py
Total time running my_sum: 0.854454040527 seconds
python profile.py  0.79s user 0.18s system 98% cpu 0.977 total

위 결과를 보면 스크립트 실행에 CPU 시간이 0.79초, 커널 기능 실행에 0.18초가 소요되어 총 0.977초가 소요됩니다.
그 중 총 시간 - (사용자 시간 + 시스템 시간) = 기타 작업의 입출력 및 시스템 실행에 소요된 시간

python timeit 모듈

을 벤치마킹에 사용할 수 있으며, 예 프로그램이 실행될 수 있는 블록 수를 확인하기 위해 프로그램이 실행되는 횟수를 편리하게 반복합니다. 자세한 내용은 이전에 작성한 글을 참고하시기 바랍니다.

cProfile

주석이 달린 사용 예를 살펴보세요.

#coding=utf8

def sum_num(max_num):
    total = 0
    for i in range(max_num):
        total += i
    return total


def test():
    total = 0
    for i in range(40000):
        total += i

    t1 = sum_num(100000)
    t2 = sum_num(200000)
    t3 = sum_num(300000)
    t4 = sum_num(400000)
    t5 = sum_num(500000)
    test2()

    return total

def test2():
    total = 0
    for i in range(40000):
        total += i

    t6 = sum_num(600000)
    t7 = sum_num(700000)

    return total


if __name__ == "__main__":
    import cProfile

    # # 直接把分析结果打印到控制台
    # cProfile.run("test()")
    # # 把分析结果保存到文件中
    # cProfile.run("test()", filename="result.out")
    # 增加排序方式
    cProfile.run("test()", filename="result.out", sort="cumulative")

cProfile은 분석 결과를 result.out 파일에 저장하는데, 바이너리 형태로 저장되어 있으니 직접 보시려면 제공된 pstats를 이용해서 보시면 됩니다.

import pstats

# 创建Stats对象
p = pstats.Stats("result.out")

# strip_dirs(): 去掉无关的路径信息
# sort_stats(): 排序,支持的方式和上述的一致
# print_stats(): 打印分析结果,可以指定打印前几行

# 和直接运行cProfile.run("test()")的结果是一样的
p.strip_dirs().sort_stats(-1).print_stats()

# 按照函数名排序,只打印前3行函数的信息, 参数还可为小数,表示前百分之几的函数信息
p.strip_dirs().sort_stats("name").print_stats(3)

# 按照运行时间和函数名进行排序
p.strip_dirs().sort_stats("cumulative", "name").print_stats(0.5)

# 如果想知道有哪些函数调用了sum_num
p.print_callers(0.5, "sum_num")

# 查看test()函数中调用了哪些函数
p.print_callees("test")

test()가 어떤 함수를 호출하는지 확인하기 위해 출력 예제를 가로채세요.

➜  python python profile.py
   Random listing order was used
   List reduced from 6 to 2 due to restriction <&#39;test&#39;>

Function              called...
                          ncalls  tottime  cumtime
profile.py:24(test2)  ->       2    0.061    0.077  profile.py:3(sum_num)
                               1    0.000    0.000  {range}
profile.py:10(test)   ->       5    0.073    0.094  profile.py:3(sum_num)
                               1    0.002    0.079  profile.py:24(test2)
                               1    0.001    0.001  {range}

profile.Profile

cProfile은 사용자 정의할 수 있는 클래스도 제공합니다. , 좀 더 자세히 분석할 수 있습니다. 자세한 내용은 설명서를 참조하세요.
형식은 다음과 같습니다: class profile.Profile(timer=None, timeunit=0.0, subcalls=True, 내장=True)
다음 예는 공식 문서에서 가져온 것입니다.

import cProfile, pstats, StringIO
pr = cProfile.Profile()
pr.enable()
# ... do something ...
pr.disable()
s = StringIO.StringIO()
sortby = &#39;cumulative&#39;
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
ps.print_stats()
print s.getvalue()

lineprofiler

lineprofiler是一个对函数进行逐行性能分析的工具,可以参见github项目说明,地址: https://github.com/rkern/line...

示例

#coding=utf8

def sum_num(max_num):
    total = 0
    for i in range(max_num):
        total += i
    return total


@profile                     # 添加@profile 来标注分析哪个函数
def test():
    total = 0
    for i in range(40000):
        total += i

    t1 = sum_num(10000000)
    t2 = sum_num(200000)
    t3 = sum_num(300000)
    t4 = sum_num(400000)
    t5 = sum_num(500000)
    test2()

    return total

def test2():
    total = 0
    for i in range(40000):
        total += i

    t6 = sum_num(600000)
    t7 = sum_num(700000)

    return total

test()

通过 kernprof 命令来注入分析,运行结果如下:

➜ kernprof -l -v profile.py
Wrote profile results to profile.py.lprof
Timer unit: 1e-06 s

Total time: 3.80125 s
File: profile.py
Function: test at line 10

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    10                                           @profile
    11                                           def test():
    12         1            5      5.0      0.0      total = 0
    13     40001        19511      0.5      0.5      for i in range(40000):
    14     40000        19066      0.5      0.5          total += i
    15
    16         1      2974373 2974373.0     78.2      t1 = sum_num(10000000)
    17         1        58702  58702.0      1.5      t2 = sum_num(200000)
    18         1        81170  81170.0      2.1      t3 = sum_num(300000)
    19         1       114901 114901.0      3.0      t4 = sum_num(400000)
    20         1       155261 155261.0      4.1      t5 = sum_num(500000)
    21         1       378257 378257.0     10.0      test2()
    22
    23         1            2      2.0      0.0      return total

hits(执行次数) 和 time(耗时) 值高的地方是有比较大优化空间的地方。

memoryprofiler

类似于"lineprofiler"对基于行分析程序内存使用情况的模块。github 地址:https://github.com/fabianp/me... 。ps:安装 psutil, 会分析的更快。

同样是上面"lineprofiler"中的代码,运行 python -m memory_profiler profile.py 命令生成结果如下:

➜ python -m memory_profiler profile.py
Filename: profile.py

Line #    Mem usage    Increment   Line Contents
================================================
    10   24.473 MiB    0.000 MiB   @profile
    11                             def test():
    12   24.473 MiB    0.000 MiB       total = 0
    13   25.719 MiB    1.246 MiB       for i in range(40000):
    14   25.719 MiB    0.000 MiB           total += i
    15
    16  335.594 MiB  309.875 MiB       t1 = sum_num(10000000)
    17  337.121 MiB    1.527 MiB       t2 = sum_num(200000)
    18  339.410 MiB    2.289 MiB       t3 = sum_num(300000)
    19  342.465 MiB    3.055 MiB       t4 = sum_num(400000)
    20  346.281 MiB    3.816 MiB       t5 = sum_num(500000)
    21  356.203 MiB    9.922 MiB       test2()
    22
    23  356.203 MiB    0.000 MiB       return total


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.