通常我使用shell命令time
。我的目的是测试数据是小型,中型,大型还是非常大的集合,多少时间和内存使用量。
用于linux或python的任何工具都可以执行此操作吗?
答案 0 :(得分:93)
查看timeit,the python profiler和pycallgraph。
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
This module使用graphviz创建如下的调用图:
您可以通过颜色轻松查看哪些路径占用时间最多。您可以使用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是你路径中的一个脚本,你可以在一个包含一些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/895245和snakeviz 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,其中包含所需的输出,如下所示:
然后您可以:
更多面向个人资料的问题: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)
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
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',
)
打印到终端并返回带有上次运行数据的词典列表。命令行入口点也可用。
如果您更改N=1000000
并重新运行