Project Euler和其他编码竞赛通常有最长的运行时间,或者人们吹嘘他们的特定解决方案运行速度有多快。对于Python,有时方法有些笨拙——即向__main__添加计时代码。

描述Python程序运行时间的好方法是什么?


当前回答

cProfile非常适合快速分析,但大多数时候它都以错误结束。函数runctx通过正确初始化环境和变量来解决这个问题,希望它对某些人有用:

import cProfile
cProfile.runctx('foo()', None, locals())

其他回答

cProfile非常适合于分析,而kcachegrind非常适合于可视化结果。中间的pyprov2calltree处理文件转换。

python -m cProfile -o script.profile script.py
pyprof2calltree -i script.profile -o script.calltree
kcachegrind script.calltree

所需的系统包:

kcachegrind(Linux)、qcachegrind(MacOs)

Ubuntu上的设置:

apt-get install kcachegrind 
pip install pyprof2calltree

结果:

cProfile非常适合快速分析,但大多数时候它都以错误结束。函数runctx通过正确初始化环境和变量来解决这个问题,希望它对某些人有用:

import cProfile
cProfile.runctx('foo()', None, locals())

找到所有时间去向的最简单快捷的方法。

1. pip install snakeviz

2. python -m cProfile -o temp.dat <PROGRAM>.py

3. snakeviz temp.dat

在浏览器中绘制饼图。最大的部分是问题函数。非常简单。

在Virtaal的源代码中,有一个非常有用的类和装饰器,它可以使分析(甚至对于特定的方法/函数)非常简单。然后可以在KCacheGrind中非常舒适地查看输出。

如果你想做一个累积分析器,意思是连续运行函数几次并观察结果的总和。

您可以使用此cumulative_profiler装饰器:

它是python>=3.6特定的,但您可以删除非本地的,因为它可以在旧版本上工作。

import cProfile, pstats

class _ProfileFunc:
    def __init__(self, func, sort_stats_by):
        self.func =  func
        self.profile_runs = []
        self.sort_stats_by = sort_stats_by

    def __call__(self, *args, **kwargs):
        pr = cProfile.Profile()
        pr.enable()  # this is the profiling section
        retval = self.func(*args, **kwargs)
        pr.disable()

        self.profile_runs.append(pr)
        ps = pstats.Stats(*self.profile_runs).sort_stats(self.sort_stats_by)
        return retval, ps

def cumulative_profiler(amount_of_times, sort_stats_by='time'):
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            nonlocal function, amount_of_times, sort_stats_by  # for python 2.x remove this row

            profiled_func = _ProfileFunc(function, sort_stats_by)
            for i in range(amount_of_times):
                retval, ps = profiled_func(*args, **kwargs)
            ps.print_stats()
            return retval  # returns the results of the function
        return wrapper

    if callable(amount_of_times):  # incase you don't want to specify the amount of times
        func = amount_of_times  # amount_of_times is the function in here
        amount_of_times = 5  # the default amount
        return real_decorator(func)
    return real_decorator

实例

剖析函数baz

import time

@cumulative_profiler
def baz():
    time.sleep(1)
    time.sleep(2)
    return 1

baz()

baz跑了5次并打印了以下内容:

         20 function calls in 15.003 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       10   15.003    1.500   15.003    1.500 {built-in method time.sleep}
        5    0.000    0.000   15.003    3.001 <ipython-input-9-c89afe010372>:3(baz)
        5    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

指定次数

@cumulative_profiler(3)
def baz():
    ...