Reputation: 21751
I want to measure the time it took to execute a function. I couldn't get timeit
to work:
import timeit
start = timeit.timeit()
print("hello")
end = timeit.timeit()
print(end - start)
Upvotes: 2171
Views: 3023201
Reputation: 1868
Here is an answer using:
time.perf_counter()
to compute time delta. It should be preferred as it is not adjustable (neither a sysadmin nor a daemon can change its value) contrary to time.time()
(see doc)import time
from collections.abc import Iterator
from contextlib import contextmanager
@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")
An example how to use it:
# Example: vector dot product computation
with time_it():
A = B = range(1_000_000)
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
Upvotes: 21
Reputation: 10355
on python3:
from time import sleep, perf_counter as pc
t0 = pc()
sleep(1)
print(pc()-t0)
elegant and short.
output:
1.001345009999568
Upvotes: 35
Reputation: 414725
Use timeit.default_timer
instead of timeit.timeit
. The former provides the best clock available on your platform and version of Python automatically:
from timeit import default_timer as timer
start = timer()
# ...
end = timer()
print(end - start) # Time in seconds, e.g. 5.38091952400282
timeit.default_timer is assigned to time.time() or time.clock() depending on OS. On Python 3.3+ default_timer is time.perf_counter() on all platforms. See Python - time.clock() vs. time.time() - accuracy?
See also:
Upvotes: 1207
Reputation: 2543
If you want to be able to time functions conveniently, you can use a simple decorator:
import time
def timing_decorator(func):
def wrapper(*args, **kwargs):
start = time.perf_counter()
original_return_val = func(*args, **kwargs)
end = time.perf_counter()
print("time elapsed in ", func.__name__, ": ", end - start, sep='')
return original_return_val
return wrapper
You can use it on a function that you want to time like this:
@timing_decorator
def function_to_time():
time.sleep(1)
function_to_time()
Any time you call function_to_time
, it will print how long it took and the name of the function being timed.
Upvotes: 22
Reputation: 7943
I'm pretty late to the party, but this approach was not covered before. When we want to benchmark manually some piece of code, we may want to find out first which of class methods eats the execution time, and this is sometimes not obvious. I have built the following metaclass to solve exactly this problem:
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)
It allows usage like the following:
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()
It prints:
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
It can be combined with other answers to replace time.time
with something more precise.
Upvotes: 1
Reputation: 500853
Use time.time()
to measure the elapsed wall-clock time between two points:
import time
start = time.time()
print("hello")
end = time.time()
print(end - start)
This gives the execution time in seconds.
Another option since Python 3.3 might be to use perf_counter
or process_time
, depending on your requirements. Before 3.3 it was recommended to use time.clock
(thanks Amber). However, it is currently deprecated:
On Unix, return the current processor time as a floating point number expressed in seconds. The precision, and in fact the very definition of the meaning of “processor time”, depends on that of the C function of the same name.
On Windows, this function returns wall-clock seconds elapsed since the first call to this function, as a floating point number, based on the Win32 function
QueryPerformanceCounter()
. The resolution is typically better than one microsecond.Deprecated since version 3.3: The behaviour of this function depends on the platform: use
perf_counter()
orprocess_time()
instead, depending on your requirements, to have a well defined behaviour.
Upvotes: 2593
Reputation: 4136
You can use Benchmark Timer (disclaimer: I'm the author):
Benchmark Timer
Use the
BenchmarkTimer
class to measure the time it takes to execute some piece of code.
This gives more flexibility than the built-in timeit function, and runs in the same scope as the rest of your code.Installation
pip install git+https://github.com/michaelitvin/benchmark-timer.git@main#egg=benchmark-timer
Usage
Single iteration example
from benchmark_timer import BenchmarkTimer import time with BenchmarkTimer(name="MySimpleCode") as tm, tm.single_iteration(): time.sleep(.3)
Output:
Benchmarking MySimpleCode... MySimpleCode benchmark: n_iters=1 avg=0.300881s std=0.000000s range=[0.300881s~0.300881s]
Multiple iterations example
from benchmark_timer import BenchmarkTimer import time with BenchmarkTimer(name="MyTimedCode", print_iters=True) as tm: for timing_iteration in tm.iterations(n=5, warmup=2): with timing_iteration: time.sleep(.1) print("\n===================\n") print("List of timings: ", list(tm.timings.values()))
Output:
Benchmarking MyTimedCode... [MyTimedCode] iter=0 took 0.099755s (warmup) [MyTimedCode] iter=1 took 0.100476s (warmup) [MyTimedCode] iter=2 took 0.100189s [MyTimedCode] iter=3 took 0.099900s [MyTimedCode] iter=4 took 0.100888s MyTimedCode benchmark: n_iters=3 avg=0.100326s std=0.000414s range=[0.099900s~0.100888s] =================== List of timings: [0.10018850000000001, 0.09990049999999995, 0.10088760000000008]
Upvotes: 0
Reputation: 366
If you use the time
module, you can get the current timestamp, and then execute your code, and get the timestamp again. Now, the time taken will be the first timestamp minus the second timestamp:
import time
first_stamp = int(round(time.time() * 1000))
# YOUR CODE GOES HERE
time.sleep(5)
second_stamp = int(round(time.time() * 1000))
# Calculate the time taken in milliseconds
time_taken = second_stamp - first_stamp
# To get time in seconds:
time_taken_seconds = round(time_taken / 1000)
print(f'{time_taken_seconds} seconds or {time_taken} milliseconds')
Upvotes: 1
Reputation: 20021
Measuring time in seconds:
from timeit import default_timer as timer
from datetime import timedelta
start = timer()
# ....
# (your code runs here)
# ...
end = timer()
print(timedelta(seconds=end-start))
Output:
0:00:01.946339
Upvotes: 247
Reputation: 9652
Since time.clock()
is deprecated as of Python 3.3, you will want to use time.perf_counter()
for system-wide timing, or time.process_time()
for process-wide timing, just the way you used to use time.clock()
:
import time
t = time.process_time()
#do some stuff
elapsed_time = time.process_time() - t
The new function process_time
will not include time elapsed during sleep.
Upvotes: 257
Reputation: 3187
The easiest way to calculate the duration of an operation:
import time
start_time = time.monotonic()
<operations, programs>
print('seconds: ', time.monotonic() - start_time)
Official docs here.
Upvotes: 67
Reputation: 13861
As a lambda, obtain time elapsed and time stamps:
import datetime
t_set = lambda: datetime.datetime.now().astimezone().replace(microsecond=0)
t_diff = lambda t: str(t_set() - t)
t_stamp = lambda t=None: str(t) if t else str(t_set())
In practice:
>>>
>>> t_set()
datetime.datetime(2021, 3, 21, 1, 25, 17, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200), 'PDT'))
>>> t = t_set()
>>> t_diff(t)
'0:00:14'
>>> t_diff(t)
'0:00:23'
>>> t_stamp()
'2021-03-21 01:25:57-07:00'
>>> t_stamp(t)
'2021-03-21 01:25:22-07:00'
>>>
Upvotes: 5
Reputation: 22552
Although it's not strictly asked in the question, it is quite often the case that you want a simple, uniform way to incrementally measure the elapsed time between several lines of code.
If you are using Python 3.8 or above, you can make use of assignment expressions (a.k.a. the walrus operator) to achieve this in a fairly elegant way:
import time
start, times = time.perf_counter(), {}
print("hello")
times["print"] = -start + (start := time.perf_counter())
time.sleep(1.42)
times["sleep"] = -start + (start := time.perf_counter())
a = [n**2 for n in range(10000)]
times["pow"] = -start + (start := time.perf_counter())
print(times)
=>
{'print': 2.193450927734375e-05, 'sleep': 1.4210970401763916, 'power': 0.005671024322509766}
Upvotes: 7
Reputation: 609
print_elapsed_time function is below
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
use it in this way
print_elapsed_time()
.... heavy jobs ...
print_elapsed_time('after heavy jobs')
.... tons of jobs ...
print_elapsed_time('after tons of jobs')
result is
after heavy jobs elapsed time: 0.39 sec
after tons of jobs elapsed time: 0.60 sec
the pros and cons of this function is that you don't need to pass start time
Upvotes: 8
Reputation: 3187
How to measure the time between two operations. Compare the time of two operations.
import time
b = (123*321)*123
t1 = time.time()
c = ((9999^123)*321)^123
t2 = time.time()
print(t2-t1)
7.987022399902344e-05
Upvotes: 13
Reputation: 68858
Here are my findings after going through many good answers here as well as a few other articles.
First, if you are debating between timeit
and time.time
, the timeit
has two advantages:
timeit
selects the best timer available on your OS and Python version.timeit
disables garbage collection, however, this is not something you may or may not want.Now the problem is that timeit
is not that simple to use because it needs setup and things get ugly when you have a bunch of imports. Ideally, you just want a decorator or use with
block and measure time. Unfortunately, there is nothing built-in available for this so you have two options:
Option 1: Use timebudget library
The timebudget is a versatile and very simple library that you can use just in one line of code after pip install.
@timebudget # Record how long this function takes
def my_method():
# my code
Option 2: Use my small module
I created below little timing utility module called timing.py. Just drop this file in your project and start using it. The only external dependency is runstats which is again small.
Now you can time any function just by putting a decorator in front of it:
import timing
@timing.MeasureTime
def MyBigFunc():
#do something time consuming
for i in range(10000):
print(i)
timing.print_all_timings()
If you want to time portion of code then just put it inside with
block:
import timing
#somewhere in my code
with timing.MeasureBlockTime("MyBlock"):
#do something time consuming
for i in range(10000):
print(i)
# rest of my code
timing.print_all_timings()
Advantages:
There are several half-backed versions floating around so I want to point out few highlights:
with timing.MeasureBlockTime() as t
and then t.elapsed
).Upvotes: 51
Reputation: 24107
Here's a pretty well documented and fully type hinted decorator I use as a general utility:
from functools import wraps
from time import perf_counter
from typing import Any, Callable, Optional, TypeVar, cast
F = TypeVar("F", bound=Callable[..., Any])
def timer(prefix: Optional[str] = None, precision: int = 6) -> Callable[[F], F]:
"""Use as a decorator to time the execution of any function.
Args:
prefix: String to print before the time taken.
Default is the name of the function.
precision: How many decimals to include in the seconds value.
Examples:
>>> @timer()
... def foo(x):
... return x
>>> foo(123)
foo: 0.000...s
123
>>> @timer("Time taken: ", 2)
... def foo(x):
... return x
>>> foo(123)
Time taken: 0.00s
123
"""
def decorator(func: F) -> F:
@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
nonlocal prefix
prefix = prefix if prefix is not None else f"{func.__name__}: "
start = perf_counter()
result = func(*args, **kwargs)
end = perf_counter()
print(f"{prefix}{end - start:.{precision}f}s")
return result
return cast(F, wrapper)
return decorator
Example usage:
from timer import timer
@timer(precision=9)
def takes_long(x: int) -> bool:
return x in (i for i in range(x + 1))
result = takes_long(10**8)
print(result)
Output:
takes_long: 4.942629056s True
The doctests can be checked with:
$ python3 -m doctest --verbose -o=ELLIPSIS timer.py
And the type hints with:
$ mypy timer.py
Upvotes: 10
Reputation: 436
based on the contextmanager solution given by https://stackoverflow.com/a/30024601/5095636, hereunder the lambda free version, as flake8 warns on the usage of lambda as per E731:
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
test:
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
Upvotes: 2
Reputation: 1776
Unit of time: measured in seconds as a float
import timeit
t = timeit.Timer('li = list(map(lambda x:x*2,[1,2,3,4,5]))')
t.timeit()
t.repeat()
>[1.2934070999999676, 1.3335035000000062, 1.422568500000125]
The repeat() method is a convenience to call timeit() multiple times and return a list of results.
repeat(repeat=3)¶
With this list we can take a mean of all times.
By default, timeit() temporarily turns off garbage collection during the timing. time.Timer() solves this problem.
Pros:
timeit.Timer() makes independent timings more comparable. The gc may be an important component of the performance of the function being measured. If so, gc(garbage collector) can be re-enabled as the first statement in the setup string. For example:
timeit.Timer('li = list(map(lambda x:x*2,[1,2,3,4,5]))',setup='gc.enable()')
Source Python Docs!
Upvotes: 3
Reputation: 844
Here's another way to do this:
>> from pytictoc import TicToc
>> t = TicToc() # create TicToc instance
>> t.tic() # Start timer
>> # do something
>> t.toc() # Print elapsed time
Elapsed time is 2.612231 seconds.
Comparing with traditional way:
>> from time import time
>> t1 = time()
>> # do something
>> t2 = time()
>> elapsed = t2 - t1
>> print('Elapsed time is %f seconds.' % elapsed)
Elapsed time is 2.612231 seconds.
Installation:
pip install pytictoc
Refer to the PyPi page for more details.
Upvotes: 69
Reputation: 4204
Time can also be measured by %timeit magic function as follow:
%timeit -t -n 1 print("hello")
n 1
is for running function only 1 time.
Upvotes: 0
Reputation: 63504
This unique class-based approach offers a printable string representation, customizable rounding, and convenient access to the elapsed time as a string or a float. It was developed with Python 3.7.
import datetime
import timeit
class Timer:
"""Measure time used."""
# Ref: https://stackoverflow.com/a/57931660/
def __init__(self, round_ndigits: int = 0):
self._round_ndigits = round_ndigits
self._start_time = timeit.default_timer()
def __call__(self) -> float:
return timeit.default_timer() - self._start_time
def __str__(self) -> str:
return str(datetime.timedelta(seconds=round(self(), self._round_ndigits)))
Usage:
# Setup timer
>>> timer = Timer()
# Access as a string
>>> print(f'Time elapsed is {timer}.')
Time elapsed is 0:00:03.
>>> print(f'Time elapsed is {timer}.')
Time elapsed is 0:00:04.
# Access as a float
>>> timer()
6.841332235
>>> timer()
7.970274425
Upvotes: 7
Reputation: 683
The timeit
module is good for timing a small piece of Python code. It can be used at least in three forms:
1- As a command-line module
python2 -m timeit 'for i in xrange(10): oct(i)'
2- For a short code, pass it as arguments.
import timeit
timeit.Timer('for i in xrange(10): oct(i)').timeit()
3- For longer code as:
import timeit
code_to_test = """
a = range(100000)
b = []
for i in a:
b.append(i*2)
"""
elapsed_time = timeit.timeit(code_to_test, number=100)/100
print(elapsed_time)
Upvotes: 0
Reputation: 429
import time
def getElapsedTime(startTime, units):
elapsedInSeconds = time.time() - startTime
if units == 'sec':
return elapsedInSeconds
if units == 'min':
return elapsedInSeconds/60
if units == 'hour':
return elapsedInSeconds/(60*60)
Upvotes: 3
Reputation: 3142
I like it simple (python 3):
from timeit import timeit
timeit(lambda: print("hello"))
Output is microseconds for a single execution:
2.430883963010274
Explanation: timeit executes the anonymous function 1 million times by default and the result is given in seconds. Therefore the result for 1 single execution is the same amount but in microseconds on average.
For slow operations add a lower number of iterations or you could be waiting forever:
import time
timeit(lambda: time.sleep(1.5), number=1)
Output is always in seconds for the total number of iterations:
1.5015795179999714
Upvotes: 18
Reputation: 10948
%load_ext snakeviz
%%snakeviz
It just takes those 2 lines of code in a Jupyter notebook, and it generates a nice interactive diagram. For example:
Here is the code. Again, the 2 lines starting with %
are the only extra lines of code needed to use snakeviz:
# !pip install snakeviz
%load_ext snakeviz
import glob
import hashlib
%%snakeviz
files = glob.glob('*.txt')
def print_files_hashed(files):
for file in files:
with open(file) as f:
print(hashlib.md5(f.read().encode('utf-8')).hexdigest())
print_files_hashed(files)
It also seems possible to run snakeviz outside notebooks. More info on the snakeviz website.
Upvotes: 15
Reputation: 23534
In addition to %timeit
in ipython you can also use %%timeit
for multi-line code snippets:
In [1]: %%timeit
...: complex_func()
...: 2 + 2 == 5
...:
...:
1 s ± 1.93 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Also it can be used in jupyter notebook the same way, just put magic %%timeit
at the beginning of cell.
Upvotes: -4
Reputation: 7577
I prefer this. timeit
doc is far too confusing.
from datetime import datetime
start_time = datetime.now()
# INSERT YOUR CODE
time_elapsed = datetime.now() - start_time
print('Time elapsed (hh:mm:ss.ms) {}'.format(time_elapsed))
Note, that there isn't any formatting going on here, I just wrote hh:mm:ss
into the printout so one can interpret time_elapsed
Upvotes: 79
Reputation: 91
Kind of a super later response, but maybe it serves a purpose for someone. This is a way to do it which I think is super clean.
import time
def timed(fun, *args):
s = time.time()
r = fun(*args)
print('{} execution took {} seconds.'.format(fun.__name__, time.time()-s))
return(r)
timed(print, "Hello")
Keep in mind that "print" is a function in Python 3 and not Python 2.7. However, it works with any other function. Cheers!
Upvotes: 9
Reputation: 616
Use profiler module. It gives a very detailed profile.
import profile
profile.run('main()')
it outputs something like:
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)
I've found it very informative.
Upvotes: 22