问题
What is the Python equivalent of Matlab's tic and toc functions?
回答1:
Apart from timeit
which ThiefMaster mentioned, a simple way to do it is just (after importing time
):
t = time.time()
# do stuff
elapsed = time.time() - t
I have a helper class I like to use:
class Timer(object):
def __init__(self, name=None):
self.name = name
def __enter__(self):
self.tstart = time.time()
def __exit__(self, type, value, traceback):
if self.name:
print('[%s]' % self.name,)
print('Elapsed: %s' % (time.time() - self.tstart))
It can be used as a context manager:
with Timer('foo_stuff'):
# do some foo
# do some stuff
Sometimes I find this technique more convenient than timeit
- it all depends on what you want to measure.
回答2:
I had the same question when I migrated to python from Matlab. With the help of this thread I was able to construct an exact analog of the Matlab tic()
and toc()
functions. Simply insert the following code at the top of your script.
import time
def TicTocGenerator():
# Generator that returns time differences
ti = 0 # initial time
tf = time.time() # final time
while True:
ti = tf
tf = time.time()
yield tf-ti # returns the time difference
TicToc = TicTocGenerator() # create an instance of the TicTocGen generator
# This will be the main function through which we define both tic() and toc()
def toc(tempBool=True):
# Prints the time difference yielded by generator instance TicToc
tempTimeInterval = next(TicToc)
if tempBool:
print( "Elapsed time: %f seconds.\n" %tempTimeInterval )
def tic():
# Records a time in TicToc, marks the beginning of a time interval
toc(False)
That's it! Now we are ready to fully use tic()
and toc()
just as in Matlab. For example
tic()
time.sleep(5)
toc() # returns "Elapsed time: 5.00 seconds."
Actually, this is more versatile than the built-in Matlab functions. Here, you could create another instance of the TicTocGenerator
to keep track of multiple operations, or just to time things differently. For instance, while timing a script, we can now time each piece of the script seperately, as well as the entire script. (I will provide a concrete example)
TicToc2 = TicTocGenerator() # create another instance of the TicTocGen generator
def toc2(tempBool=True):
# Prints the time difference yielded by generator instance TicToc2
tempTimeInterval = next(TicToc2)
if tempBool:
print( "Elapsed time 2: %f seconds.\n" %tempTimeInterval )
def tic2():
# Records a time in TicToc2, marks the beginning of a time interval
toc2(False)
Now you should be able to time two separate things: In the following example, we time the total script and parts of a script separately.
tic()
time.sleep(5)
tic2()
time.sleep(3)
toc2() # returns "Elapsed time 2: 5.00 seconds."
toc() # returns "Elapsed time: 8.00 seconds."
Actually, you do not even need to use tic()
each time. If you have a series of commands that you want to time, then you can write
tic()
time.sleep(1)
toc() # returns "Elapsed time: 1.00 seconds."
time.sleep(2)
toc() # returns "Elapsed time: 2.00 seconds."
time.sleep(3)
toc() # returns "Elapsed time: 3.00 seconds."
# and so on...
I hope that this is helpful.
回答3:
The absolute best analog of tic and toc would be to simply define them in python.
def tic():
#Homemade version of matlab tic and toc functions
import time
global startTime_for_tictoc
startTime_for_tictoc = time.time()
def toc():
import time
if 'startTime_for_tictoc' in globals():
print "Elapsed time is " + str(time.time() - startTime_for_tictoc) + " seconds."
else:
print "Toc: start time not set"
Then you can use them as:
tic()
# do stuff
toc()
回答4:
Usually, IPython's %time
, %timeit
, %prun
and %lprun
(if one has line_profiler
installed) satisfy my profiling needs quite well. However, a use case for tic-toc
-like functionality arose when I tried to profile calculations that were interactively driven, i.e., by the user's mouse motion in a GUI. I felt like spamming tic
s and toc
s in the sources while testing interactively would be the fastest way to reveal the bottlenecks. I went with Eli Bendersky's Timer
class, but wasn't fully happy, since it required me to change the indentation of my code, which can be inconvenient in some editors and confuses the version control system. Moreover, there may be the need to measure the time between points in different functions, which wouldn't work with the with
statement. After trying lots of Python cleverness, here is the simple solution that I found worked best:
from time import time
_tstart_stack = []
def tic():
_tstart_stack.append(time())
def toc(fmt="Elapsed: %s s"):
print fmt % (time() - _tstart_stack.pop())
Since this works by pushing the starting times on a stack, it will work correctly for multiple levels of tic
s and toc
s. It also allows one to change the format string of the toc
statement to display additional information, which I liked about Eli's Timer
class.
For some reason I got concerned with the overhead of a pure Python implementation, so I tested a C extension module as well:
#include <Python.h>
#include <mach/mach_time.h>
#define MAXDEPTH 100
uint64_t start[MAXDEPTH];
int lvl=0;
static PyObject* tic(PyObject *self, PyObject *args) {
start[lvl++] = mach_absolute_time();
Py_RETURN_NONE;
}
static PyObject* toc(PyObject *self, PyObject *args) {
return PyFloat_FromDouble(
(double)(mach_absolute_time() - start[--lvl]) / 1000000000L);
}
static PyObject* res(PyObject *self, PyObject *args) {
return tic(NULL, NULL), toc(NULL, NULL);
}
static PyMethodDef methods[] = {
{"tic", tic, METH_NOARGS, "Start timer"},
{"toc", toc, METH_NOARGS, "Stop timer"},
{"res", res, METH_NOARGS, "Test timer resolution"},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC
inittictoc(void) {
Py_InitModule("tictoc", methods);
}
This is for MacOSX, and I have omitted code to check if lvl
is out of bounds for brevity. While tictoc.res()
yields a resolution of about 50 nanoseconds on my system, I found that the jitter of measuring any Python statement is easily in the microsecond range (and much more when used from IPython). At this point, the overhead of the Python implementation becomes negligible, so that it can be used with the same confidence as the C implementation.
I found that the usefulness of the tic-toc
-approach is practically limited to code blocks that take more than 10 microseconds to execute. Below that, averaging strategies like in timeit
are required to get a faithful measurement.
回答5:
Just in case someone is interested. Based on all the other answers I wrote a tictoc class that has the best of them all.
The link on github is here.
You can also use pip to get it.
pip install ttictoc
As of how to use it:
Import it
from ttictoc import TicToc
Using the 'with' statement
Without creating any object you can time your code as follow.
with TicToc('name'):
some code...
# Prints the elapsed time
Or by creating an object you can do de same.
t = TicToc('name')
with t:
some code...
# Prints the elapsed time
Calling tic toc explicitly
You can also call the tic toc explicitply as shown bellow.
t = TicToc('name')
t.tic()
some code...
t.toc()
print(t.elapsed)
With indentation
If you want to time multiple levels of your code, you can also do it by setting 'indentation' to True.
t = TicToc(,indentation=True)
t.tic()
some code1...
t.tic()
some code2...
t.tic()
some code3...
t.toc()
print('time for code 3 ',t.elapsed)
t.toc()
print('time for code 2 with code 3 ',t.elapsed)
t.toc()
print('time for code 1 with code 2 and 3 ',t.elapsed)
Arguments
The class has 3 arguments: name,method, and indentation.
- name: It's the name of the object. It's not required.
- method: Indicates which method should be used to get the time.
- indentation: Allows to use the same object several times, in different indentations to time.
The method argument can be either int, str, or your method choice. If it's a string, the valid values are time, perf_counter, and process_time. If it's an integer, the valid values are 0, 1, and 2.
- time or 0: time.time
- perf_counter or 1: time.perf_counter
- process_time or 2: time.process_time
If python version >= 3.7: - time_ns or 3: time.time_ns - perf_counter_ns or 4: time.perf_counter_ns - process_time_ns or 5: time.process_time_ns
In case you prefere to use other method you just do (using as example time.clock:
TicToc(method=time.clock)
The class is the following:
import sys
import time
class TicToc(object):
"""
Counts the elapsed time.
"""
def __init__(self,name='',method='time',indentation=False):
"""
Args:
name (str): Just informative, not needed
method (int|str|ftn|clss): Still trying to understand the default
options. 'time' uses the 'real wold' clock, while the other
two use the cpu clock. If you want to use your own method, do it
through this argument
Valid int values:
0: time.time | 1: time.perf_counter | 2: time.proces_time
if python version >= 3.7:
3: time.time_ns | 4: time.perf_counter_ns | 5: time.proces_time_ns
Valid str values:
'time': time.time | 'perf_counter': time.perf_counter
'process_time': time.proces_time
if python version >= 3.7:
'time_ns': time.time_ns | 'perf_counter_ns': time.perf_counter_ns
'proces_time_ns': time.proces_time_ns
Others:
Whatever you want to use as time.time
indentation (bool): Allows to do tic toc with indentation with a single object.
If True, you can put several tics using the same object, and each toc will
correspond to the respective tic.
If False, it will only register one single tic, and return the respective
elapsed time of the future tocs.
"""
self.name = name
self.indentation = indentation
if self.indentation:
self.tstart = []
self.__measure = 's' # seconds
self.__vsys = sys.version_info
if self.__vsys[0]>2 and self.__vsys[1]>=7:
# If python version is greater or equal than 3.7
if type(method) is int:
if method==0: method = 'time'
elif method==1: method = 'perf_counter'
elif method==2: method = 'process_time'
elif method==3: method = 'time_ns'
elif method==3: method = 'perf_counter_ns'
elif method==4: method = 'process_time_ns'
else:
import warnings
msg = "Value '{0}' is not a valid option. Using 'time' instead.".format(method)
warnings.warn(msg,Warning)
method = 'time'
if type(method) is str:
if method=='time': self.get_time = time.time
elif method=='perf_counter': self.get_time = time.perf_counter
elif method=='process_time': self.get_time = time.process_time
elif method=='time_ns': self.get_time = time.time_ns, self.__measure = 'ns' # nanoseconds
elif method=='perf_counter_ns': self.get_time = time.perf_counter_ns, self.__measure = 'ns' # nanoseconds
elif method=='process_time_ns': self.get_time = time.process_time_ns, self.__measure = 'ns' # nanoseconds
else:
import warnings
msg = "Value '{0}' is not a valid option. Using 'time' instead.".format(method)
warnings.warn(msg,Warning)
self.get_time = time.time
else:
self.get_time = method
else:
# If python vesion is lower than 3.7
if type(method) is int:
if method==0: method = 'time'
elif method==1: method = 'perf_counter'
elif method==2: method = 'process_time'
else:
import warnings
msg = "Value '{0}' is not a valid option. Using 'time' instead.".format(method)
warnings.warn(msg,Warning)
method = 'time'
if type(method) is str:
if method=='time': self.get_time = time.time
elif method=='perf_counter': self.get_time = time.perf_counter
elif method=='process_time': self.get_time = time.process_time
else:
import warnings
msg = "Value '{0}' is not a valid option. Using 'time' instead.".format(method)
warnings.warn(msg,Warning)
self.get_time = time.time
else:
self.get_time = method
def __enter__(self):
if self.indentation:
self.tstart.append(self.get_time())
else:
self.tstart = self.get_time()
def __exit__(self,type,value,traceback):
self.tend = self.get_time()
if self.indentation:
self.elapsed = self.tend - self.tstart.pop()
else:
self.elapsed = self.tend - self.tstart
if self.name!='': name = '[{}] '.format(self.name)
else: name = self.name
print('{0}Elapsed time: {1} ({2})'.format(name,self.elapsed,self.__measure))
def tic(self):
if self.indentation:
self.tstart.append(self.get_time())
else:
self.tstart = self.get_time()
def toc(self):
self.tend = self.get_time()
if self.indentation:
if len(self.tstart)>0:
self.elapsed = self.tend - self.tstart.pop()
else:
self.elapsed = None
else:
self.elapsed = self.tend - self.tstart
回答6:
I have just created a module [tictoc.py] for achieving nested tic tocs, which is what Matlab does.
from time import time
tics = []
def tic():
tics.append(time())
def toc():
if len(tics)==0:
return None
else:
return time()-tics.pop()
And it works this way:
from tictoc import tic, toc
# This keeps track of the whole process
tic()
# Timing a small portion of code (maybe a loop)
tic()
# -- Nested code here --
# End
toc() # This returns the elapse time (in seconds) since the last invocation of tic()
toc() # This does the same for the first tic()
I hope it helps.
回答7:
Have a look at the timeit module. It's not really equivalent but if the code you want to time is inside a function you can easily use it.
回答8:
This can also be done using a wrapper. Very general way of keeping time.
The wrapper in this example code wraps any function and prints the amount of time needed to execute the function:
def timethis(f):
import time
def wrapped(*args, **kwargs):
start = time.time()
r = f(*args, **kwargs)
print "Executing {0} took {1} seconds".format(f.func_name, time.time()-start)
return r
return wrapped
@timethis
def thistakestime():
for x in range(10000000):
pass
thistakestime()
回答9:
I changed @Eli Bendersky's answer a little bit to use the ctor __init__()
and dtor __del__()
to do the timing, so that it can be used more conveniently without indenting the original code:
class Timer(object):
def __init__(self, name=None):
self.name = name
self.tstart = time.time()
def __del__(self):
if self.name:
print '%s elapsed: %.2fs' % (self.name, time.time() - self.tstart)
else:
print 'Elapsed: %.2fs' % (time.time() - self.tstart)
To use, simple put Timer("blahblah") at the beginning of some local scope. Elapsed time will be printed at the end of the scope:
for i in xrange(5):
timer = Timer("eigh()")
x = numpy.random.random((4000,4000));
x = (x+x.T)/2
numpy.linalg.eigh(x)
print i+1
timer = None
It prints out:
1
eigh() elapsed: 10.13s
2
eigh() elapsed: 9.74s
3
eigh() elapsed: 10.70s
4
eigh() elapsed: 10.25s
5
eigh() elapsed: 11.28s
回答10:
Updating Eli's answer to Python 3:
class Timer(object):
def __init__(self, name=None, filename=None):
self.name = name
self.filename = filename
def __enter__(self):
self.tstart = time.time()
def __exit__(self, type, value, traceback):
message = 'Elapsed: %.2f seconds' % (time.time() - self.tstart)
if self.name:
message = '[%s] ' % self.name + message
print(message)
if self.filename:
with open(self.filename,'a') as file:
print(str(datetime.datetime.now())+": ",message,file=file)
Just like Eli's, it can be used as a context manager:
import time
with Timer('Count'):
for i in range(0,10_000_000):
pass
Output:
[Count] Elapsed: 0.27 seconds
I have also updated it to print the units of time reported (seconds) and trim the number of digits as suggested by Can, and with the option of also appending to a log file. You must import datetime to use the logging feature:
import time
import datetime
with Timer('Count', 'log.txt'):
for i in range(0,10_000_000):
pass
回答11:
Building on Stefan and antonimmo's answers, I ended up putting
def Tictoc():
start_stack = []
start_named = {}
def tic(name=None):
if name is None:
start_stack.append(time())
else:
start_named[name] = time()
def toc(name=None):
if name is None:
start = start_stack.pop()
else:
start = start_named.pop(name)
elapsed = time() - start
return elapsed
return tic, toc
in a utils.py
module, and I use it with a
from utils import Tictoc
tic, toc = Tictoc()
This way
- you can simply use
tic()
,toc()
and nest them like in Matlab - alternatively, you can name them:
tic(1)
,toc(1)
ortic('very-important-block')
,toc('very-important-block')
and timers with different names won't interfere - importing them this way prevents interference between modules using it.
(here toc does not print the elapsed time, but returns it.)
来源:https://stackoverflow.com/questions/5849800/what-is-the-python-equivalent-of-matlabs-tic-and-toc-functions