我想测量执行一个函数所花费的时间。我没时间工作:
import timeit
start = timeit.timeit()
print("hello")
end = timeit.timeit()
print(end - start)
我想测量执行一个函数所花费的时间。我没时间工作:
import timeit
start = timeit.timeit()
print("hello")
end = timeit.timeit()
print(end - start)
当前回答
我参加聚会已经很晚了,但这种方法以前没有涉及过。当我们想要手动对某段代码进行基准测试时,我们可能需要首先找出哪些类方法占用了执行时间,这有时并不明显。我构建了以下元类来解决这个问题:
from __future__ import annotations
from functools import wraps
from time import time
from typing import Any, Callable, TypeVar, cast
F = TypeVar('F', bound=Callable[..., Any])
def timed_method(func: F, prefix: str | None = None) -> F:
prefix = (prefix + ' ') if prefix else ''
@wraps(func)
def inner(*args, **kwargs): # type: ignore
start = time()
try:
ret = func(*args, **kwargs)
except BaseException:
print(f'[ERROR] {prefix}{func.__qualname__}: {time() - start}')
raise
print(f'{prefix}{func.__qualname__}: {time() - start}')
return ret
return cast(F, inner)
class TimedClass(type):
def __new__(
cls: type[TimedClass],
name: str,
bases: tuple[type[type], ...],
attrs: dict[str, Any],
**kwargs: Any,
) -> TimedClass:
for name, attr in attrs.items():
if isinstance(attr, (classmethod, staticmethod)):
attrs[name] = type(attr)(timed_method(attr.__func__))
elif isinstance(attr, property):
attrs[name] = property(
timed_method(attr.fget, 'get') if attr.fget is not None else None,
timed_method(attr.fset, 'set') if attr.fset is not None else None,
timed_method(attr.fdel, 'del') if attr.fdel is not None else None,
)
elif callable(attr):
attrs[name] = timed_method(attr)
return super().__new__(cls, name, bases, attrs)
它允许如下使用:
class MyClass(metaclass=TimedClass):
def foo(self):
print('foo')
@classmethod
def bar(cls):
print('bar')
@staticmethod
def baz():
print('baz')
@property
def prop(self):
print('prop')
@prop.setter
def prop(self, v):
print('fset')
@prop.deleter
def prop(self):
print('fdel')
c = MyClass()
c.foo()
c.bar()
c.baz()
c.prop
c.prop = 2
del c.prop
MyClass.bar()
MyClass.baz()
它打印:
foo
MyClass.foo: 1.621246337890625e-05
bar
MyClass.bar: 4.5299530029296875e-06
baz
MyClass.baz: 4.291534423828125e-06
prop
get MyClass.prop: 3.814697265625e-06
fset
set MyClass.prop: 3.5762786865234375e-06
fdel
del MyClass.prop: 3.5762786865234375e-06
bar
MyClass.bar: 3.814697265625e-06
baz
MyClass.baz: 4.0531158447265625e-06
它可以与其他答案相结合,以更精确的方式代替time.time。
其他回答
使用探查器模块。它提供了非常详细的概况。
import profile
profile.run('main()')
它输出类似于:
5 function calls in 0.047 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 :0(exec)
1 0.047 0.047 0.047 0.047 :0(setprofile)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
0 0.000 0.000 profile:0(profiler)
1 0.000 0.000 0.047 0.047 profile:0(main())
1 0.000 0.000 0.000 0.000 two_sum.py:2(twoSum)
我发现它很有启发性。
print_elapsed_time函数如下
def print_elapsed_time(prefix=''):
e_time = time.time()
if not hasattr(print_elapsed_time, 's_time'):
print_elapsed_time.s_time = e_time
else:
print(f'{prefix} elapsed time: {e_time - print_elapsed_time.s_time:.2f} sec')
print_elapsed_time.s_time = e_time
用这种方式
print_elapsed_time()
.... heavy jobs ...
print_elapsed_time('after heavy jobs')
.... tons of jobs ...
print_elapsed_time('after tons of jobs')
结果是
after heavy jobs elapsed time: 0.39 sec
after tons of jobs elapsed time: 0.60 sec
这个函数的优点和缺点是你不需要经过开始时间
以下是一个答案,使用:
对代码片段进行计时的简洁上下文管理器time.perf_counter()计算时间增量。与time.time()相反,它是不可调整的(sysadmin和守护程序都不能更改其值),因此应该首选它(参见文档)python3.10+(因为键入,但可以很容易地适应以前的版本)
import time
from contextlib import contextmanager
from typing import Iterator
@contextmanager
def time_it() -> Iterator[None]:
tic: float = time.perf_counter()
try:
yield
finally:
toc: float = time.perf_counter()
print(f"Computation time = {1000*(toc - tic):.3f}ms")
如何使用它的示例:
# Example: vector dot product computation
with time_it():
A = B = range(1000000)
dot = sum(a*b for a,b in zip(A,B))
# Computation time = 95.353ms
附录
import time
# to check adjustability
assert time.get_clock_info('time').adjustable
assert time.get_clock_info('perf_counter').adjustable is False
基于https://stackoverflow.com/a/30024601/5095636,以下为无lambda版本,如flake8根据E731对lambda使用的警告:
from contextlib import contextmanager
from timeit import default_timer
@contextmanager
def elapsed_timer():
start_time = default_timer()
class _Timer():
start = start_time
end = default_timer()
duration = end - start
yield _Timer
end_time = default_timer()
_Timer.end = end_time
_Timer.duration = end_time - start_time
测试:
from time import sleep
with elapsed_timer() as t:
print("start:", t.start)
sleep(1)
print("end:", t.end)
t.start
t.end
t.duration
下面是另一个用于计时代码的上下文管理器-
用法:
from benchmark import benchmark
with benchmark("Test 1+1"):
1+1
=>
Test 1+1 : 1.41e-06 seconds
或者,如果您需要时间值
with benchmark("Test 1+1") as b:
1+1
print(b.time)
=>
Test 1+1 : 7.05e-07 seconds
7.05233786763e-07
基准.py:
from timeit import default_timer as timer
class benchmark(object):
def __init__(self, msg, fmt="%0.3g"):
self.msg = msg
self.fmt = fmt
def __enter__(self):
self.start = timer()
return self
def __exit__(self, *args):
t = timer() - self.start
print(("%s : " + self.fmt + " seconds") % (self.msg, t))
self.time = t
改编自http://dabeaz.blogspot.fr/2010/02/context-manager-for-timing-benchmarks.html