有没有简单的方法来基准python脚本?

时间:2009-10-20 07:40:54

标签: python unix shell benchmarking

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

用于linux或python的任何工具都可以执行此操作吗?

12 个答案:

答案 0 :(得分:93)

查看timeitthe python profilerpycallgraph

timeit

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"))

基本上,您可以将python代码作为字符串参数传递,它将以指定的次数运行并打印执行时间。来自文档的重要部分:

  

timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000)

     
    

使用给定语句 setup 创建Timer实例     代码和计时器功能并运行其timeit方法     号码执行。

  

......和:

  

<强> Timer.timeit(number=1000000)

     
    

主语句的时间数量执行。这将执行设置     语句一次,然后返回执行main所需的时间     声明多次,以秒为单位测量为浮点数。     参数是循环次数,默认为1     百万。主要语句,设置语句和计时器功能     要使用的是传递给构造函数。

         

注意

         
      

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

             
        

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

      
    
  

仿形

分析将为您提供更多更详细的了解正在发生的事情。这是the official docs中的“即时示例”:

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的输出,请查看this post

pycallgraph

This module使用graphviz创建如下的调用图:

callgraph example

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

pycallgraph graphviz -- ./mypythonscript.py

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

答案 1 :(得分:25)

我使用一个简单的装饰器为func计时

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

答案 2 :(得分:12)

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

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

答案 3 :(得分:10)

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

答案 4 :(得分:3)

请查看nose及其中一个插件,特别是this one

安装后,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)"

答案 5 :(得分:3)

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

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

运行pip install:

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

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

答案 6 :(得分:1)

snakeviz用于cProfile的交互式查看器

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

https://stackoverflow.com/a/1593034/895245snakeviz was mentioned in a comment提到了cProfile,但我想进一步强调它。

仅通过查看cprofile / pstats输出来调试程序性能非常困难,因为它们只能使每个函数合计时间。

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

这正是snakeviz通过其默认“冰柱”视图提供的内容。

首先,您必须将cProfile数据转储到二进制文件中,然后才能在该文件上进行蛇行搜索

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

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

enter image description here

然后您可以:

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

更多面向个人资料的问题:How can you profile a Python script?

答案 7 :(得分:0)

请注意,<li><a href="<?php echo $newSite; ?>"></a></li> 非常慢,在我的中型处理器上初始化(或运行该功能)需要12秒钟。您可以测试这个可接受的答案

timeit

为简单起见,我将使用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 代替,在我的PC上它将返回结果time

0.0

答案 8 :(得分:0)

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

例如:

%timeit a = 1

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

答案 9 :(得分:0)

基于刘丹云的回答,有一些方便的功能,也许对某人有用。

def stopwatch(repeat=1, autorun=True):
    """
    stopwatch decorator to calculate the total time of a function
    """
    import timeit
    import functools
    
    def outer_func(func):
        @functools.wraps(func)
        def time_func(*args, **kwargs):
            t1 = timeit.default_timer()
            for _ in range(repeat):
                r = func(*args, **kwargs)
            t2 = timeit.default_timer()
            print(f"Function={func.__name__}, Time={t2 - t1}")
            return r
        
        if autorun:
            try:
                time_func()
            except TypeError:
                raise Exception(f"{time_func.__name__}: autorun only works with no parameters, you may want to use @stopwatch(autorun=False)") from None
        
        return time_func
    
    if callable(repeat):
        func = repeat
        repeat = 1
        return outer_func(func)
    
    return outer_func

一些测试:

def is_in_set(x):
    return x in {"linux", "darwin"}

def is_in_list(x):
    return x in ["linux", "darwin"]

@stopwatch
def run_once():
    import time
    time.sleep(0.5)

@stopwatch(autorun=False)
def run_manually():
    import time
    time.sleep(0.5)

run_manually()

@stopwatch(repeat=10000000)
def repeat_set():
    is_in_set("windows")
    is_in_set("darwin")

@stopwatch(repeat=10000000)
def repeat_list():
    is_in_list("windows")
    is_in_list("darwin")

@stopwatch
def should_fail(x):
    pass

结果:

Function=run_once, Time=0.5005391679987952
Function=run_manually, Time=0.500624185999186
Function=repeat_set, Time=1.7064883739985817
Function=repeat_list, Time=1.8905151920007484
Traceback (most recent call last):
  (some more traceback here...)
Exception: should_fail: autorun only works with no parameters, you may want to use @stopwatch(autorun=False)

答案 10 :(得分:0)

line_profiler(逐行执行时间)

安装

pip install line_profiler

用法

  • 在函数前添加一个 @profile 装饰器。例如:
@profile
def function(base, index, shift):
    addend = index << shift
    result = base + addend
    return result
  • 使用命令 kernprof -l <file_name> 创建 line_profiler 的实例。例如:
kernprof -l test.py

kernprof 将在成功时打印 Wrote profile results to <file_name>.lprof。例如:

Wrote profile results to test.py.lprof
  • 使用命令 python -m line_profiler <file_name>.lprof 打印基准测试结果。例如:
python -m line_profiler test.py.lprof

您将看到有关每行代码的详细信息:

Timer unit: 1e-06 s

Total time: 0.0021632 s
File: test.py
Function: function at line 1

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     1                                           @profile
     2                                           def function(base, index, shift):
     3      1000        796.4      0.8     36.8      addend = index << shift
     4      1000        745.9      0.7     34.5      result = base + addend
     5      1000        620.9      0.6     28.7      return result

memory_profiler(逐行内存使用)

安装

pip install memory_profiler

用法

  • 在函数前添加一个 @profile 装饰器。例如:
@profile
def function():
    result = []
    for i in range(10000):
        result.append(i)
    return result
  • 使用命令 python -m memory_profiler <file_name> 打印基准测试结果。例如:
python -m memory_profiler test.py

您将看到有关每行代码的详细信息:

Filename: test.py

Line #    Mem usage    Increment  Occurences   Line Contents
============================================================
     1   40.246 MiB   40.246 MiB           1   @profile
     2                                         def function():
     3   40.246 MiB    0.000 MiB           1       result = []
     4   40.758 MiB    0.008 MiB       10001       for i in range(10000):
     5   40.758 MiB    0.504 MiB       10000           result.append(i)
     6   40.758 MiB    0.000 MiB           1       return result

良好做法

多次调用一个函数以尽量减少对环境的影响。

答案 11 :(得分:-1)

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

# 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',
)  

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

enter image description here

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

enter image description here