1814

What I want is to start counting time somewhere in my code and then get the passed time, to measure the time it took to execute few function. I think I'm using the timeit module wrong, but the docs are just confusing for me.

import timeit

start = timeit.timeit()
print("hello")
end = timeit.timeit()
print(end - start)
ruohola
  • 19,113
  • 6
  • 51
  • 82
gilbert8
  • 18,173
  • 3
  • 13
  • 3
  • 2
    timeit.timeit() prints the time that it takes to execute its argument, which is "pass" by default. you have to instead use start= time.time() end = time.time() – Llopeth Mar 31 '21 at 11:49

39 Answers39

2159

If you just want to measure the elapsed wall-clock time between two points, you could use time.time():

import time

start = time.time()
print("hello")
end = time.time()
print(end - start)

This gives the execution time in seconds.

Another option since 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() or process_time() instead, depending on your requirements, to have a well defined behaviour.

NPE
  • 464,258
  • 100
  • 912
  • 987
  • 34
    and for microseconds, use datetime.time() – Inca Sep 10 '11 at 09:33
  • 147
    (For performance measurement, `time.clock()` is actually preferred, since it can't be interfered with if the system clock gets messed with, but `.time()` does mostly accomplish the same purpose.) – Amber Sep 10 '11 at 09:34
  • 4
    I think that python -mtimeit is way better as it runs more times and it is build as a native way to measure time in python – Visgean Skeloru Feb 03 '14 at 22:06
  • 7
    Is there a nice way of converting resulting execturion time in seconds to something like HH:MM::SS? – Danijel Feb 04 '16 at 10:05
  • 3
    @Amber: `time.clock()` meaning (what is measured) and its accuracy and precision (resolution) differ between platforms. [The docs](https://docs.python.org/3/library/time.html#time.clock) no longer recommend `time.clock()` for benchmarking. [`timeit.default_timer()` could be used instead.](http://stackoverflow.com/a/25823885/4279) – jfs Apr 06 '16 at 19:14
  • 17
    @Danijel: `print(timedelta(seconds=execution_time))`. Though it is a separate question. – jfs Apr 06 '16 at 19:15
  • 3
    time.clock() didnt work for me. See the timeit answer by @J.F.Sebastian – AlejandroVD May 09 '16 at 20:52
  • 2
    I prefer this. Timeit doc is far too confusing. `from datetime import datetime startTime= datetime.now() # INSERT YOUR CODE timeElapsed=datetime.now()-startTime print('Time elpased (hh:mm:ss.ms) {}'.format(timeElapsed))` – Reddspark Aug 14 '17 at 06:27
  • 7
    time.clock() is deprecated at of Python 3.3 https://docs.python.org/3.3/library/time.html#time.clock – noah Jun 06 '19 at 19:19
  • 1
    @VisgeanSkeloru But sometimes you simply want the time that a particular command took in a particular instance, rather than profiling. Running it n times to get more accuracy takes n times longer after all. What is "better" depends on the situation. – RoG Sep 06 '19 at 12:40
  • 5
    `time.time()` is a bad idea because the system clock can be reset which will make you go back in time. `time.monotonic()` takes care of this (monotonic = it only goes forward). `time.perf_counter()` is also monotonic but has even higher accuracy, so this is recommended for wall-clock time. – xjcl May 31 '20 at 11:17
  • 2
    The function `time.clock()` has been removed, after having been deprecated since Python 3.3. `time.perf_counter()` or `time.process_time()` can be used instead. Ref: https://stackoverflow.com/a/58569410/6701627 – thepunitsingh Dec 08 '20 at 18:48
1008

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:

jfs
  • 374,366
  • 172
  • 933
  • 1,594
  • 64
    Excellent answer - using **timeit** will produce far more accurate results since it will automatically account for things like garbage collection and OS differences – lkgarrison Dec 11 '16 at 03:16
  • 1
    This gives time in ms or seconds? – Katie Feb 08 '17 at 16:52
  • 3
    @KhushbooTiwari in fractional seconds. – jfs Feb 08 '17 at 16:59
  • 9
    I think this note from the official documentation needs to be added `default_timer() measurations can be affected by other programs running on the same machine, so the best thing to do when accurate timing is necessary is to repeat the timing a few times and use the best time. The -r option is good for this; the default of 3 repetitions is probably enough in most cases. On Unix, you can use time.clock() to measure CPU time.` – KGS Jul 06 '17 at 10:00
  • 1
    @KGS: Performance measurement is very tricky in a subtle way (it is easy to mislead yourself). There are many other remarks that could be relevant here. Follow the links in the answer. You might be also interested in the [`perf` module (nonexistent at the time of the answer)](https://perf.readthedocs.io/en/latest/) that provides the same interface but it sometimes makes different from the `timeit` module decisions about how to measure time performance. – jfs Jul 06 '17 at 13:52
  • 2
    I dont like how the import took place. "from timeit import default_timer as timer". Got lost while reading the code. Could have been clearer without having the "as timer" part. – Basil Musa Oct 22 '17 at 10:27
  • Concise lambda: ``` lang-python from timeit import default_timer as timer timed = lambda f, s=None: (f(), round(timer() - s, 3)) if s else timed(f, timer()) (result, elapsed) = timed(some_function) ``` – Tapomay Feb 26 '20 at 01:36
  • 1
    Yes, @BasilMusa, I don't know why peeps sometimes like to rename canonical library identifiers, especially to something that is identical to another canonical library identiier altogether! Talk about generating deliberate ambiguity. Just to reduce line length? Perhaps they're still struggling with the ridiculous 80 character line limit, for when peeps used to have vewy vewy small monitors. Just hike it to 120. I recommend 160, oh yeah! – NeilG May 27 '22 at 01:35
  • @NeilG part of the `import` functionality acts like `=` as in `x=1`. `timer` is used for the same reason `x` may be used instead of `1`. Think about it. – jfs Jun 03 '22 at 21:01
210

Python 3 only:

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.

phoenix
  • 5,738
  • 3
  • 33
  • 41
Pierre Prinetti
  • 8,092
  • 6
  • 31
  • 48
  • 50
    [Use `timeit.default_timer`](http://stackoverflow.com/a/25823885/4279) instead of `time.perf_counter`. The former will choose the appropriate timer to measure the time performance tuned for your platform and Python version. `process_time()` does *not* include the time during sleep and therefore it is not appropriate to measure elapsed time. – jfs Feb 22 '15 at 14:30
  • 3
    I'm using the implementation suggested by Pierre, are the values given in seconds? – ugotchi Aug 12 '16 at 08:01
  • 2
    This answer seems off-topic (well, the question wasn't very specific). There are two "time" measurement : wall-clock time between two points, of the cpu consumption of the process. – Franklin Piat Jul 26 '19 at 09:08
  • 2
    @fjs `timeit.default_timer` uses `time.perf_counter` in Python >=3.3 https://docs.python.org/3/library/timeit.html#timeit.default_timer – ruohola Feb 05 '21 at 15:11
  • 2
    elapsed_time return 0.07812 for example. How do I interpret that? Would a second be 1.000 so my script ran in 7,812 milliseconds? – Vega Sep 29 '21 at 10:58
186

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
Gal Bracha
  • 16,762
  • 11
  • 64
  • 81
100

Given a function you'd like to time,

test.py:

def foo(): 
    # print "hello"   
    return "hello"

the easiest way to use timeit is to call it from the command line:

% python -mtimeit -s'import test' 'test.foo()'
1000000 loops, best of 3: 0.254 usec per loop

Do not try to use time.time or time.clock (naively) to compare the speed of functions. They can give misleading results.

PS. Do not put print statements in a function you wish to time; otherwise the time measured will depend on the speed of the terminal.

Community
  • 1
  • 1
unutbu
  • 777,569
  • 165
  • 1,697
  • 1,613
86

It's fun to do this with a context-manager that automatically remembers the start time upon entry to a with block, then freezes the end time on block exit. With a little trickery, you can even get a running elapsed-time tally inside the block from the same context-manager function.

The core library doesn't have this (but probably ought to). Once in place, you can do things like:

with elapsed_timer() as elapsed:
    # some lengthy code
    print( "midpoint at %.2f seconds" % elapsed() )  # time so far
    # other lengthy code

print( "all done at %.2f seconds" % elapsed() )

Here's contextmanager code sufficient to do the trick:

from contextlib import contextmanager
from timeit import default_timer

@contextmanager
def elapsed_timer():
    start = default_timer()
    elapser = lambda: default_timer() - start
    yield lambda: elapser()
    end = default_timer()
    elapser = lambda: end-start

And some runnable demo code:

import time

with elapsed_timer() as elapsed:
    time.sleep(1)
    print(elapsed())
    time.sleep(2)
    print(elapsed())
    time.sleep(3)

Note that by design of this function, the return value of elapsed() is frozen on block exit, and further calls return the same duration (of about 6 seconds in this toy example).

Nicholas Riley
  • 42,081
  • 5
  • 99
  • 124
gojomo
  • 47,667
  • 12
  • 80
  • 104
  • 2
    Other context manager example: http://dabeaz.blogspot.fr/2010/02/context-manager-for-timing-benchmarks.html – Jérôme May 03 '16 at 14:03
  • 1
    @Jérôme nice example - I adapted it as another answer - http://stackoverflow.com/a/41408510/243392 – Brian Burns Dec 31 '16 at 13:05
68

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

Saugat
  • 1,239
  • 13
  • 20
Reddspark
  • 5,514
  • 7
  • 38
  • 53
  • I was told that timeit calculates the CPU time, does datetime also take into account CPU time used? Are these the same thing? – Sreehari R Dec 29 '17 at 07:21
  • 9
    It's risky to measure elapsed time this way because datetime.now() can change between the two calls for reasons like network time syncing, daylight savings switchover or the user twiddling the clock. – user1318499 Jul 22 '19 at 02:20
  • From Shital Shah's answer: "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." – mic Aug 01 '20 at 05:33
61

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.

Mingwei He
  • 764
  • 5
  • 10
  • 18
    It would be good to explain the advantage of using this library over other approaches. – hlg Jul 08 '19 at 05:50
  • The nested functionality is actually broken. I opened an issue describing where the problem in the code is but the repo hasn't been maintained in a year so I wouldn't expect a change. – PetarMI Jul 10 '19 at 14:04
  • I find the nesting a little confusing. If I were to come across `t.tic()` buried in the code, it's up to me the developer to keep a mental list of where in the series I should expect this to be. Do you find yourself setting up nests or just multiple tictocs? – ScottieB Oct 17 '19 at 14:55
  • 1
    @PetarMI : FYI, I just fixed the issue with `ttictoc`. Quite a mess I had, but it should be good now. – H. Sánchez Apr 06 '20 at 00:10
  • @hlg If I remember correctly, MATLAB uses functions with similar names to time stuff. So I guess the advantage is the resemblance, for people who liked this in MATLAB but switched to Python. – Henri Nov 19 '20 at 11:04
58

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.

user1318499
  • 1,165
  • 9
  • 29
Wojciech Moszczyński
  • 2,275
  • 16
  • 23
  • @user1318499 it's not that it returns negative values, it can return a lower value than a previous call. https://docs.python.org/3/library/time.html#time.time – Paolo Apr 13 '21 at 21:06
  • 2
    It is better to use `time.monotonic_ns()`, see https://docs.python.org/3/library/time.html#time.monotonic_ns – alexsmail Oct 26 '21 at 17:14
48

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:

  1. timeit selects the best timer available on your OS and Python version.
  2. 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:

  1. Use timer from timeit instead of time.time for reasons described earlier.
  2. You can disable GC during timing if you want.
  3. Decorator accepts functions with named or unnamed params.
  4. Ability to disable printing in block timing (use with timing.MeasureBlockTime() as t and then t.elapsed).
  5. Ability to keep gc enabled for block timing.
Shital Shah
  • 55,892
  • 12
  • 218
  • 175
  • Regarding "Ability to disable printing in block timing (use `with utils.MeasureBlockTime() as t` and then `t.elapsed`).": this doesn't work as is, as `t` is `None`. I think `__enter__` needs to return `self`, and to disable printing, we have to construct it as `utils.MeasureBlockTime(no_print=True)`. – mic Aug 01 '20 at 06:24
  • @mic - thanks for pointing this out. I've updated the answer with this and several other enhancements. – Shital Shah Aug 02 '20 at 04:56
30

Using time.time to measure execution gives you the overall execution time of your commands including running time spent by other processes on your computer. It is the time the user notices, but is not good if you want to compare different code snippets / algorithms / functions / ...

More information on timeit:

If you want a deeper insight into profiling:

Update: I used http://pythonhosted.org/line_profiler/ a lot during the last year and find it very helpfull and recommend to use it instead of Pythons profile module.

Community
  • 1
  • 1
rocksportrocker
  • 7,034
  • 2
  • 30
  • 46
21

Here's another context manager for timing code -

Usage:

from benchmark import benchmark

with benchmark("Test 1+1"):
    1+1
=>
Test 1+1 : 1.41e-06 seconds

or, if you need the time value

with benchmark("Test 1+1") as b:
    1+1
print(b.time)
=>
Test 1+1 : 7.05e-07 seconds
7.05233786763e-07

benchmark.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

Adapted from http://dabeaz.blogspot.fr/2010/02/context-manager-for-timing-benchmarks.html

Brian Burns
  • 17,878
  • 8
  • 77
  • 67
21

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.

Leonid Ganeline
  • 589
  • 5
  • 16
20

The python cProfile and pstats modules offer great support for measuring time elapsed in certain functions without having to add any code around the existing functions.

For example if you have a python script timeFunctions.py:

import time

def hello():
    print "Hello :)"
    time.sleep(0.1)

def thankyou():
    print "Thank you!"
    time.sleep(0.05)

for idx in range(10):
    hello()

for idx in range(100):
    thankyou()

To run the profiler and generate stats for the file you can just run:

python -m cProfile -o timeStats.profile timeFunctions.py

What this is doing is using the cProfile module to profile all functions in timeFunctions.py and collecting the stats in the timeStats.profile file. Note that we did not have to add any code to existing module (timeFunctions.py) and this can be done with any module.

Once you have the stats file you can run the pstats module as follows:

python -m pstats timeStats.profile

This runs the interactive statistics browser which gives you a lot of nice functionality. For your particular use case you can just check the stats for your function. In our example checking stats for both functions shows us the following:

Welcome to the profile statistics browser.
timeStats.profile% stats hello
<timestamp>    timeStats.profile

         224 function calls in 6.014 seconds

   Random listing order was used
   List reduced from 6 to 1 due to restriction <'hello'>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       10    0.000    0.000    1.001    0.100 timeFunctions.py:3(hello)

timeStats.profile% stats thankyou
<timestamp>    timeStats.profile

         224 function calls in 6.014 seconds

   Random listing order was used
   List reduced from 6 to 1 due to restriction <'thankyou'>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      100    0.002    0.000    5.012    0.050 timeFunctions.py:7(thankyou)

The dummy example does not do much but give you an idea of what can be done. The best part about this approach is that I dont have to edit any of my existing code to get these numbers and obviously help with profiling.

sanchitarora
  • 872
  • 9
  • 18
  • All this is fine, but AFAICT this still measures CPU time, not wall clock time. – ShreevatsaR Apr 10 '14 at 14:34
  • 2
    Actually there is some confusion; it appears cProfile does look at wall-clock time by default. I've upvoted your answer. – ShreevatsaR Apr 10 '14 at 14:42
  • FYI: If you get `python -m pstats timeStats.profile ValueError: bad marshal data (unknown type code)` check your python version you are running. I got this when i ran `python3 -m cProfile...` and `python -m pstats`. My mistake but got me for a second, so, I wanted to share `don't forget consistency`. =) – JayRizzo Oct 26 '18 at 05:51
20

Here is a tiny timer class that returns "hh:mm:ss" string:

class Timer:
  def __init__(self):
    self.start = time.time()

  def restart(self):
    self.start = time.time()

  def get_time_hhmmss(self):
    end = time.time()
    m, s = divmod(end - self.start, 60)
    h, m = divmod(m, 60)
    time_str = "%02d:%02d:%02d" % (h, m, s)
    return time_str

Usage:

# Start timer
my_timer = Timer()

# ... do something

# Get time string:
time_hhmmss = my_timer.get_time_hhmmss()
print("Time elapsed: %s" % time_hhmmss )

# ... use the timer again
my_timer.restart()

# ... do something

# Get time:
time_hhmmss = my_timer.get_time_hhmmss()

# ... etc
Shai
  • 102,241
  • 35
  • 217
  • 344
Danijel
  • 7,354
  • 18
  • 64
  • 114
  • And now with f-strings and [`format specifications`](https://docs.python.org/3/library/string.html#format-specification-mini-language) included: `time_str = f"{h:02d}:{m:02d}:{s:02d}"` – howdoicode Jan 23 '21 at 22:00
18

(With Ipython only) you can use %timeit to measure average processing time:

def foo():
    print "hello"

and then:

%timeit foo()

the result is something like:

10000 loops, best of 3: 27 µs per loop
raacer
  • 4,923
  • 3
  • 26
  • 45
Eyal Ch
  • 8,734
  • 5
  • 40
  • 52
  • 4
    It worth to mention it is possible to pass flags to %timeit, for example -n specifies how many times the code should be repeated. – raacer Dec 15 '16 at 12:50
18

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
David
  • 2,682
  • 29
  • 15
12

on python3:

from time import sleep, perf_counter as pc
t0 = pc()
sleep(1)
print(pc()-t0)

elegant and short.

DmitrySemenov
  • 7,916
  • 12
  • 56
  • 87
11

One more way to use timeit:

from timeit import timeit

def func():
    return 1 + 1

time = timeit(func, number=1)
print(time)
raacer
  • 4,923
  • 3
  • 26
  • 45
11

To get insight on every function calls recursively, do:

%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:

enter image description here

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.

Guillaume Chevalier
  • 8,155
  • 5
  • 47
  • 74
11

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

Wojciech Moszczyński
  • 2,275
  • 16
  • 23
  • easy to read, easy to use, accurate enough for approx tests or comparisons. But, as I cannot seem to edit at time of writing, to, 'compare the time of two operations', this should have `t0 = time.time()` I feel after import line. Then `print(t1 -t0)` is first operation time. 2 times are needed to compare 2 operations. – Will Croxford Dec 08 '21 at 14:28
10

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
ruohola
  • 19,113
  • 6
  • 51
  • 82
  • 1
    This is super cool, thank you for sharing. I have not encountered the typing library or the nonlocal keyword -- fun to find new things to learn about. I'm having trouble wrapping my head around this: `Callable[[AnyF], AnyF]`. What does it mean? – Danny Jun 03 '20 at 11:17
  • 1
    @Danny On the top I've defined the type alias `AnyF` to mean `Callable[..., Any]`, so `AnyF` is a function that can take any amount of any type arguments and return anything. So `Callable[[AnyF], AnyF]` would expand to `Callable[[Callable[..., Any]], Callable[..., Any]]`. This is the type of the return value of `timer` aka the full type of `decorator`. It is a function that takes any kind of function as its only argument and returns any kind of function. – ruohola Jun 03 '20 at 12:42
  • 1
    Thanks for the explanation! I'm still trying to fully wrap my head around the internals of decorators. This helped a lot! – Danny Jun 03 '20 at 14:04
9

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!

8

You can use timeit.

Here is an example on how to test naive_func that takes parameter using Python REPL:

>>> import timeit                                                                                         

>>> def naive_func(x):                                                                                    
...     a = 0                                                                                             
...     for i in range(a):                                                                                
...         a += i                                                                                        
...     return a                                                                                          

>>> def wrapper(func, *args, **kwargs):                                                                   
...     def wrapper():                                                                                    
...         return func(*args, **kwargs)                                                                  
...     return wrapper                                                                                    

>>> wrapped = wrapper(naive_func, 1_000)                                                                  

>>> timeit.timeit(wrapped, number=1_000_000)                                                              
0.4458435332577161  

You don't need wrapper function if function doesn't have any parameters.

Vlad Bezden
  • 72,691
  • 22
  • 233
  • 168
8

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

Myeongsik Joo
  • 519
  • 6
  • 7
7

We can also convert time into human-readable time.

import time, datetime

start = time.clock()

def num_multi1(max):
    result = 0
    for num in range(0, 1000):
        if (num % 3 == 0 or num % 5 == 0):
            result += num

    print "Sum is %d " % result

num_multi1(1000)

end = time.clock()
value = end - start
timestamp = datetime.datetime.fromtimestamp(value)
print timestamp.strftime('%Y-%m-%d %H:%M:%S')
Ninjakannon
  • 3,551
  • 6
  • 50
  • 70
Kamlesh Verma
  • 81
  • 1
  • 1
7

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}
Lee Netherton
  • 19,809
  • 12
  • 63
  • 99
6

I made a library for this, if you want to measure a function you can just do it like this


from pythonbenchmark import compare, measure
import time

a,b,c,d,e = 10,10,10,10,10
something = [a,b,c,d,e]

@measure
def myFunction(something):
    time.sleep(0.4)

@measure
def myOptimizedFunction(something):
    time.sleep(0.2)

myFunction(input)
myOptimizedFunction(input)

https://github.com/Karlheinzniebuhr/pythonbenchmark

Karl
  • 433
  • 1
  • 6
  • 11
6

If you want to be able to time functions conveniently, you can use a simple decorator:

def timing_decorator(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        original_return_val = func(*args, **kwargs)
        end = time.time()
        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)

Then any time you call function_to_time, it will print how long it took and the name of the function being timed.

Daniel Giger
  • 1,269
  • 13
  • 17
  • Is there a Python2.x way of doing this without having to import `print_function` from `__future__`? I tried to use `join` but I don't understand it well enough to get it to work. – frakman1 Aug 08 '20 at 04:27
  • UPDATE. I figured it out and used this: `print(''.join(["time elapsed in ",(func.__name__),": ",str(end - start)]))` – frakman1 Aug 08 '20 at 04:41
4

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
Asclepius
  • 49,954
  • 14
  • 144
  • 128
  • 1
    This is simple and excellent - easy to code; I'm surprised that (a) this kind of functionality isn't present in ANY of the existing Python profilers; and (b) that this answer, including a simple class that can be copied-and-pasted, wasn't offered to this question years ago with many more upvotes. – Dan Nissenbaum Oct 30 '21 at 07:36
4

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'
>>> 
ingyhere
  • 10,457
  • 3
  • 35
  • 46
  • I can't get this to work. `unsupported operand type(s) for -: 'datetime.datetime' and 'function'` – MEMark Oct 05 '21 at 08:38
  • @MEMark Uncertainty here without seeing the code, but note that this was made for Python 3. The error means a the wrong datatype is being used in place of a `datetime` object. Could the code be trying `t_diff(t_diff())` or `t_diff(t())` instead of `t_diff(t)` (where `t` is a scalar set earlier as `t = t_set()`)? – ingyhere Oct 05 '21 at 14:37
3
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)
Sky
  • 339
  • 2
  • 12
  • 2
    Don't do this because `time.time()` doesn't necessarily increment uniformly. You can get a negative duration if there's a daylight savings adjustment or whatever. Replace `time.time()` with `time.monotonic()`. – user1318499 Jun 19 '20 at 04:59
3

Measure execution time of small code snippets.

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!

Aditya Patnaik
  • 1,150
  • 13
  • 25
2

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
Xiang ZHU
  • 33
  • 6
0

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)
alshaboti
  • 555
  • 7
  • 17
0

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.

Punit Vara
  • 3,294
  • 1
  • 15
  • 30
0

For Python 3

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')
0

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]
Michael Litvin
  • 3,662
  • 1
  • 29
  • 38
-3

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.

vishes_shell
  • 20,161
  • 6
  • 65
  • 77