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

noo*_*omz 71 python unix shell benchmarking

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

用于linux或python的任何工具都可以做到这一点?

exh*_*uma 100

看一下timeit,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"))
Run Code Online (Sandbox Code Playgroud)

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

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

Timer使用给定语句,设置 代码和 计时器函数创建实例,并timeit使用数字执行运行其方法 .

......和:

Timer.timeit(number=1000000)

主要陈述的时间编号执行.这将执行一次setup语句,然后返回执行主语句多次所需的时间,以秒为单位测量为浮点数.参数是通过循环的次数,默认为一百万.要使用的主语句,setup语句和timer函数将传递给构造函数.

注意

默认情况下,在计时期间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")')
Run Code Online (Sandbox Code Playgroud)

哪个会给你:

      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)
Run Code Online (Sandbox Code Playgroud)

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

另外,为了掌握输出profile,请看看这篇文章

pycallgraph

此模块使用graphviz创建如下所示的调用图:

调用图例子

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

pycallgraph graphviz -- ./mypythonscript.py
Run Code Online (Sandbox Code Playgroud)

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

  • 如果使用cProfile,还可以选择分析整个脚本,并使用`python -m cProfile -o results.prof myscript.py`将结果保存到文件中.然后可以使用`snakeviz results.prof`通过名为[SnakeViz](https://jiffyclub.github.io/snakeviz/)的程序在浏览器中非常好地呈现oputput文件. (9认同)

Dan*_*Liu 26

我使用一个简单的装饰器为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
Run Code Online (Sandbox Code Playgroud)


Sam*_*ins 13

timeit模块是缓慢的,怪异的,所以我写了这一点:

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

例:

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

对我来说,它说:

python can do 40925 os.listdir('/') per second
Run Code Online (Sandbox Code Playgroud)

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

  • @exhuma,我忘记了细节,也许我的评估很仓促!我想我说"很奇怪",因为它需要两个代码块作为字符串(而不是函数/ lambda).但是当我计算非常短时间的代码片段时,我可以看到它的价值.我想我说"慢",因为它默认为1,000,000循环而我没有看到如何调整它!我喜欢我的代码已经按照代表的数量来划分.但是timeit无疑是一个更好的解决方案,我为解散它而道歉. (6认同)

Mar*_*ing 10

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


ToT*_*ire 9

line_profiler(逐行执行时间)

安装

pip install line_profiler
Run Code Online (Sandbox Code Playgroud)

用法

  • @profile在函数之前添加一个装饰器。例如:
pip install line_profiler
Run Code Online (Sandbox Code Playgroud)
  • 使用命令kernprof -l <file_name>创建 line_profiler 的实例。例如:
kernprof -l test.py
Run Code Online (Sandbox Code Playgroud)

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

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

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

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
Run Code Online (Sandbox Code Playgroud)

memory_profiler(逐行内存使用情况)

安装

pip install memory_profiler
Run Code Online (Sandbox Code Playgroud)

用法

  • @profile在函数之前添加一个装饰器。例如:
@profile
def function(base, index, shift):
    addend = index << shift
    result = base + addend
    return result
Run Code Online (Sandbox Code Playgroud)
  • 使用命令python -m memory_profiler <file_name>打印基准测试结果。例如:
python -m memory_profiler test.py
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

良好实践

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


Cir*_*四事件 8

snakevizcProfile 的交互式查看器

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

/sf/answers/111512411/中提到了 cProfile ,评论中提到了 Snakeviz,但我想进一步强调它。

仅通过查看cprofile/pstats输出来调试程序性能是非常困难的,因为它们只能开箱即用地计算每个函数的总时间。

然而,我们真正需要的是查看包含每个调用的堆栈跟踪的嵌套视图,以便轻松找到主要瓶颈。

这正是 Snakeviz 通过其默认的“冰柱”视图提供的功能。

首先,您必须将 cProfile 数据转储到二进制文件,然后您可以对其进行蛇形可视化

pip install -u snakeviz
python -m cProfile -o results.prof myscript.py
snakeviz results.prof
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述

然后你可以:

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

更多面向分析的问题:如何分析 Python 脚本?


Joh*_*ann 7

内存分析器满足您的所有内存需求。

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

运行 pip 安装:

pip install memory_profiler
Run Code Online (Sandbox Code Playgroud)

导入库:

import memory_profiler
Run Code Online (Sandbox Code Playgroud)

为您要分析的项目添加装饰器:

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

if __name__ == '__main__':
    my_func()
Run Code Online (Sandbox Code Playgroud)

执行代码:

python -m memory_profiler example.py
Run Code Online (Sandbox Code Playgroud)

接收输出:

 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
Run Code Online (Sandbox Code Playgroud)

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


v.g*_*ets 5

如果您不想为 timeit 编写样板代码并轻松分析结果,请查看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',
)  
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述

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

在此输入图像描述