有没有简单的方法可以对python脚本进行基准测试?


80

通常我使用shell命令time。我的目的是测试数据是小数据集,小数据集,大数据集还是非常大的数据集,以及使用多少时间和内存。

有任何适用于linux的工具或仅适用于python的工具吗?

Answers:


118

看看timeitpython profilerpycallgraph。还要确保通过nikicc提及“ SnakeViz ”来查看下面的评论。它为您提供了概要分析数据的另一种可视化效果,可能会有所帮助。

时间

def test():
    """Stupid test function"""
    lst = []
    for i in range(100):
        lst.append(i)

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("test()", setup="from __main__ import test"))

    # For Python>=3.5 one can also write:
    print(timeit.timeit("test()", globals=locals()))

本质上,您可以将它作为字符串参数传递给python代码,它将在指定的时间运行并显示执行时间。文档中的重要内容:

timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)Timer使用给定的语句,设置 代码和 计时器函数 创建实例,并timeit通过数字执行运行其方法 。可选的globals参数指定在其中执行代码的名称空间。

...和:

Timer.timeit(number=1000000)主语句的 时间编号执行。这将执行一次setup语句,然后返回执行主语句所需的时间(以秒为单位,以浮点数表示)。参数是循环的次数,默认为一百万次。将要使用的主语句,设置语句和计时器函数传递给构造函数。

注意: 默认情况下,在计时期间timeit暂时关闭garbage collection。这种方法的优势在于,它使独立计时更具可比性。这个缺点是GC可能是被测功能性能的重要组成部分。如果是这样,则可以将GC作为设置字符串中的第一条语句重新启用。例如:

timeit.Timer('for i in xrange(10): oct(i)', 'gc.enable()').timeit()

剖析

分析将使您对发生的事情更详细的了解。这是官方文档中的“即时示例” :

import cProfile
import re
cProfile.run('re.compile("foo|bar")')

这会给你:

      197 function calls (192 primitive calls) in 0.002 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.000    0.000    0.001    0.001 <string>:1(<module>)
     1    0.000    0.000    0.001    0.001 re.py:212(compile)
     1    0.000    0.000    0.001    0.001 re.py:268(_compile)
     1    0.000    0.000    0.000    0.000 sre_compile.py:172(_compile_charset)
     1    0.000    0.000    0.000    0.000 sre_compile.py:201(_optimize_charset)
     4    0.000    0.000    0.000    0.000 sre_compile.py:25(_identityfunction)
   3/1    0.000    0.000    0.000    0.000 sre_compile.py:33(_compile)

这两个模块都应该使您了解在哪里寻找瓶颈。

另外,要掌握的输出profile,请看一下这篇文章

pycallgraph

该模块使用graphviz创建如下的调用图:

通话图示例

您可以按颜色轻松查看哪些路径消耗最多的时间。您可以使用pycallgraph API或使用打包的脚本来创建它们:

pycallgraph graphviz -- ./mypythonscript.py

开销是相当可观的。因此,对于已经长时间运行的流程,创建图形可能需要一些时间。


10
如果使用cProfile,则还有一个选项来分析整个脚本,然后使用将结果保存到文件中python -m cProfile -o results.prof myscript.py。然后,可以通过一个名为SnakeViz的程序在浏览器中很好地呈现oputput文件,该程序使用snakeviz results.prof
nikicc

26

我使用一个简单的装饰器来计时功能

def st_time(func):
    """
        st decorator to calculate the total time of a func
    """

    def st_func(*args, **keyArgs):
        t1 = time.time()
        r = func(*args, **keyArgs)
        t2 = time.time()
        print "Function=%s, Time=%s" % (func.__name__, t2 - t1)
        return r

    return st_func

当然会打印“ Function =%s,Time =%s”%(func .__ name__,t2-t1)。Thanx,真的很方便
user1941126 2014年

15

timeit模块缓慢而奇怪,所以我这样写:

def timereps(reps, func):
    from time import time
    start = time()
    for i in range(0, reps):
        func()
    end = time()
    return (end - start) / reps

例:

import os
listdir_time = timereps(10000, lambda: os.listdir('/'))
print "python can do %d os.listdir('/') per second" % (1 / listdir_time)

对我来说,它说:

python can do 40925 os.listdir('/') per second

这是一种原始的基准测试,但已经足够了。


7
@exhuma,我忘了细节,也许我急于评估!我认为我说“怪异”是因为它需要两个代码块作为字符串(而不是函数/ lambda)。但是,我可以看到在计时非常短的代码段时的价值。我猜我说“慢”是因为它默认为1,000,000个循环,我没有考虑如何调整它!我喜欢我的代码已经除以代表次数。但是毫无疑问,timeit无疑是一个更好的解决方案,对此我深表歉意。
山姆·沃特金斯

11

我通常会快速time ./script.py查看需要多长时间。但这并没有显示您的内存,至少不是默认情况。您可以/usr/bin/time -v ./script.py用来获取很多信息,包括内存使用情况。


1
请记住,此命令/usr/bin/time及其-v选项在许多发行版中不是默认提供的,必须安装。sudo apt-get install time在debian,ubuntu等中使用pacman -S timearchlinux
Rui Andrada

5

Memory Profiler可满足您所有的内存需求。

https://pypi.python.org/pypi/memory_profiler

运行pip安装:

pip install memory_profiler

导入库:

import memory_profiler

将装饰器添加到要配置的项目中:

@profile
def my_func():
    a = [1] * (10 ** 6)
    b = [2] * (2 * 10 ** 7)
    del b
    return a

if __name__ == '__main__':
    my_func()

执行代码:

python -m memory_profiler example.py

接收输出:

 Line #    Mem usage  Increment   Line Contents
 ==============================================
 3                           @profile
 4      5.97 MB    0.00 MB   def my_func():
 5     13.61 MB    7.64 MB       a = [1] * (10 ** 6)
 6    166.20 MB  152.59 MB       b = [2] * (2 * 10 ** 7)
 7     13.61 MB -152.59 MB       del b
 8     13.61 MB    0.00 MB       return a

示例来自上面链接的文档。


3

看看鼻子和它的一个插件,这个特别是。

安装完成后,nose是路径中的脚本,您可以在包含一些python脚本的目录中进行调用:

$: nosetests

这将在当前目录中的所有python文件中查找并执行它可以识别为测试的任何功能:例如,它可以将名称中带有单词test_的任何功能识别为测试。

因此,您可以仅创建一个名为test_yourfunction.py的python脚本,并在其中编写如下代码:

$: cat > test_yourfunction.py

def test_smallinput():
    yourfunction(smallinput)

def test_mediuminput():
    yourfunction(mediuminput)

def test_largeinput():
    yourfunction(largeinput)

那你得跑

$: nosetest --with-profile --profile-stats-file yourstatsprofile.prof testyourfunction.py

并使用以下python行读取配置文件:

python -c "import hotshot.stats ; stats = hotshot.stats.load('yourstatsprofile.prof') ; stats.sort_stats('time', 'calls') ; stats.print_stats(200)"

在我看来,这和标准python库中的事件探查器一样。测试不是问题的话题。加:nose依靠热点。自Python 2.5起,它不再维护,仅保留用于“特殊用途”
2009年

1

注意timeit是非常慢的,在我的中型处理器上初始化(或运行该功能)需要12秒钟。您可以测试此可接受的答案

def test():
    lst = []
    for i in range(100):
        lst.append(i)

if __name__ == '__main__':
    import timeit
    print(timeit.timeit("test()", setup="from __main__ import test")) # 12 second

为了简单起见,我将使用time它在我的PC上返回结果0.0

import time

def test():
    lst = []
    for i in range(100):
        lst.append(i)

t1 = time.time()

test()

result = time.time() - t1
print(result) # 0.000000xxxx

timeit运行您的功能很多次,平均出噪声。重复次数是一个选项,请参阅在python中运行时间进行基准测试或此问题的已接受答案的后半部分。
彼得·科德斯

1

快速测试任何功能的简单方法是使用以下语法: %timeit my_code

例如 :

%timeit a = 1

13.4 ns ± 0.781 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)

1

snakeviz cProfile的交互式查看器

https://github.com/jiffyclub/snakeviz/

CPROFILE在提到https://stackoverflow.com/a/1593034/895245snakeviz在评论中提到,但我想进一步突出它。

仅通过查看就很难调试程序性能 cprofile/pstats输出,因为它们只能使每个函数的总时间开箱即用。

但是,我们通常真正需要的是看到一个嵌套视图,其中包含每个调用的堆栈跟踪,以便轻松地实际找到主要瓶颈。

这正是snakeviz通过其默认“ icicle”视图提供的功能。

首先,您必须将cProfile数据转储到二进制文件,然后可以在该文件上使用snakeviz

pip install -u snakeviz
python -m cProfile -o results.prof myscript.py
snakeviz results.prof

这将打印出一个指向stdout的URL,您可以在浏览器中打开该URL,其中包含所需的输出,如下所示:

在此处输入图片说明

然后您可以:

  • 将鼠标悬停在每个框上可查看包含该功能的文件的完整路径
  • 单击一个框以使该框显示在顶部,作为放大的一种方式

面向配置文件的更多问题:如何配置Python脚本?


-1

如果您不希望为timeit编写样板代码,并且不易分析结果,请查看基准测试。此外,它还保存了以前运行的历史记录,因此很容易在开发过程中比较相同的功能。

# pip install benchmarkit

from benchmarkit import benchmark, benchmark_run

N = 10000
seq_list = list(range(N))
seq_set = set(range(N))

SAVE_PATH = '/tmp/benchmark_time.jsonl'

@benchmark(num_iters=100, save_params=True)
def search_in_list(num_items=N):
    return num_items - 1 in seq_list

@benchmark(num_iters=100, save_params=True)
def search_in_set(num_items=N):
    return num_items - 1 in seq_set

benchmark_results = benchmark_run(
   [search_in_list, search_in_set],
   SAVE_PATH,
   comment='initial benchmark search',
)  

打印到终端并返回带有上次运行数据的词典列表。命令行入口点也可用。

在此处输入图片说明

如果您更改N=1000000并重新运行

在此处输入图片说明

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.