Matlabの ticおよびtoc関数 と同等のPythonとは何ですか?
ThiefMasterが言及したtimeit
は別として、それを行う簡単な方法は(time
をインポートした後)です。
t = time.time()
# do stuff
elapsed = time.time() - t
使用したいヘルパークラスがあります。
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))
コンテキストマネージャーとして使用できます。
with Timer('foo_stuff'):
# do some foo
# do some stuff
時々、この手法はtimeit
より便利だと思うことがあります-それはすべてあなたが測定したいものに依存します。
Matlabからpythonに移行したときにも同じ質問がありました。このスレッドの助けを借りて、Matlab tic()
およびtoc()
関数のexact類似物を作成することができました。スクリプトの先頭に次のコードを挿入するだけです。
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)
それでおしまい!これで、Matlabと同じようにtic()
とtoc()
を完全に使用する準備が整いました。例えば
tic()
time.sleep(5)
toc() # returns "Elapsed time: 5.00 seconds."
実際、これは組み込みのMatlab関数よりも汎用性があります。ここでは、TicTocGenerator
の別のインスタンスを作成して、複数の操作を追跡したり、時間を変えたりすることができます。たとえば、スクリプトのタイミングを計る際に、スクリプト全体と同様に、スクリプトの各部分を個別に計時できるようになりました。 (具体例を提供します)
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)
これで、2つの別々の時間を計測できるはずです。次の例では、スクリプト全体とスクリプトの一部を別々に計測します。
tic()
time.sleep(5)
tic2()
time.sleep(3)
toc2() # returns "Elapsed time 2: 5.00 seconds."
toc() # returns "Elapsed time: 8.00 seconds."
実際、毎回tic()
を使用する必要さえありません。時間を計る一連のコマンドがある場合は、次のように記述できます。
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...
これが役立つことを願っています。
Ticとtocの絶対的な最良のアナログは、単純に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"
その後、次のように使用できます。
tic()
# do stuff
toc()
通常、IPythonの%time
、%timeit
、%prun
、および%lprun
(line_profiler
がインストールされている場合)は、プロファイリングのニーズを十分に満たします。ただし、GUIでのユーザーのマウスの動きなど、インタラクティブに駆動される計算のプロファイルを作成しようとすると、tic-toc
のような機能のユースケースが発生しました。ソースでtic
sとtoc
sをスパミングすると、インタラクティブにテストすることがボトルネックを発見する最も速い方法になると感じました。私はEli BenderskyのTimer
クラスを使用しましたが、コードのインデントを変更する必要があり、一部のエディターでは不便でバージョン管理システムが混乱するため、完全に満足していませんでした。さらに、異なる関数のポイント間の時間を測定する必要がある場合がありますが、これはwith
ステートメントでは機能しません。多くのPython賢さを試した後、ここで私が見つけた最も簡単な解決策を示します。
from time import time
_tstart_stack = []
def tic():
_tstart_stack.append(time())
def toc(fmt="Elapsed: %s s"):
print fmt % (time() - _tstart_stack.pop())
これはスタックの開始時間をプッシュすることで機能するため、tic
sおよびtoc
sの複数のレベルで正しく機能します。また、toc
ステートメントのフォーマット文字列を変更して、EliのTimer
クラスについて気に入った追加情報を表示することもできます。
何らかの理由で、純粋なPython実装のオーバーヘッドが心配になったため、C拡張モジュールもテストしました。
#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);
}
これはMacOSX用であり、lvl
が簡潔のために範囲外であるかどうかをチェックするコードを省略しています。 tictoc.res()
は私のシステムで約50ナノ秒の解像度を生成しますが、Pythonステートメントの測定のジッターはマイクロ秒の範囲に簡単に収まることがわかりました(IPythonから使用した場合はさらに多くなります)。この時点で、Python実装のオーバーヘッドは無視できるようになるため、C実装と同じ信頼性で使用できます。
tic-toc
- approachの有用性は、実行に10マイクロ秒以上かかるコードブロックに実質的に限定されていることがわかりました。その下では、timeit
のような平均化戦略が忠実な測定を得るために必要です。
誰かが興味を持っている場合に備えて。他のすべての答えに基づいて、私はそれらすべての中で最高のtictocクラスを書きました。
Githubのリンクは こちら です
また、pipを使用して取得することもできます。
pip install ttictoc
使用方法に関して:
from ttictoc import TicToc
オブジェクトを作成せずに、次のようにコードの時間を調整できます。
with TicToc('name'):
some code...
# Prints the elapsed time
または、オブジェクトを作成することで同じことができます。
t = TicToc('name')
with t:
some code...
# Prints the elapsed time
以下に示すように、tic tocを明示的に呼び出すこともできます。
t = TicToc('name')
t.tic()
some code...
t.toc()
print(t.elapsed)
With indentation
複数レベルのコードを計測したい場合は、「インデント」を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)
クラスには、name、method、およびindentationの3つの引数があります。
メソッドの引数は、int、str、または選択したメソッドのいずれかです。文字列の場合、有効な値はtime、perf_counter、およびprocess_timeです。整数の場合、有効な値は0、1、および2です。
pythonバージョン> = 3.7の場合:-time_nsまたは3:time.time_ns-perf_counter_nsまたは4:time.perf_counter_ns-process_time_nsまたは5:time.process_time_ns
他の方法を使用したい場合は、time.clockを使用してください:
TicToc(method=time.clock)
クラスは次のとおりです。
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
ネストされたチックのtocsを達成するためのモジュール[tictoc.py]を作成しました。これはMatlabが行っていることです。
from time import time
tics = []
def tic():
tics.append(time())
def toc():
if len(tics)==0:
return None
else:
return time()-tics.pop()
そして、それはこのように機能します:
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()
役に立てば幸いです。
timeit
モジュールをご覧ください。実際には同等ではありませんが、時間を計りたいコードが関数内にある場合、簡単に使用できます。
@Eli Benderskyの答えを少し変更して、タイミングを行うためにctor __init__()
とdtor __del__()
を使用して、元のコードをインデントすることなくより便利に使用できるようにしました。
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)
使用するには、ローカルスコープの先頭にTimer( "blahblah")を置くだけです。経過時間は、スコープの最後に出力されます:
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
それは印刷されます:
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
これは、ラッパーを使用して行うこともできます。時間を管理する非常に一般的な方法。
このサンプルコードのラッパーは、任意の関数をラップし、関数の実行に必要な時間を出力します。
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()
更新 Eliの答え 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)
Eliのように、コンテキストマネージャーとして使用できます。
import time
with Timer('Count'):
for i in range(0,10_000_000):
pass
出力:
[Count] Elapsed: 0.27 seconds
また、報告された時間の単位(秒)を印刷し、Canによって提案された桁数をトリムするように更新しました。また、ログファイルに追加するオプションもあります。ロギング機能を使用するには、datetimeをインポートする必要があります。
import time
import datetime
with Timer('Count', 'log.txt'):
for i in range(0,10_000_000):
pass
ステファンとアントニンモの答えに基づいて、私は最終的に
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
utils.py
モジュールで、私はそれを
from utils import Tictoc
tic, toc = Tictoc()
こちらです
tic()
、toc()
を使用して、Matlabのようにネストできます。tic(1)
、toc(1)
またはtic('very-important-block')
、toc('very-important-block')
および異なる名前のタイマーは干渉しません(ここでtocは経過時間を出力しませんが、それを返します。)