web-dev-qa-db-ja.com

Matlabのticおよびtoc関数に相当するPythonとは何ですか?

Matlabの ticおよびtoc関数 と同等のPythonとは何ですか?

83
Alex

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より便利だと思うことがあります-それはすべてあなたが測定したいものに依存します。

138
Eli Bendersky

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

これが役立つことを願っています。

23
Benben

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()
19
GuestPoster

通常、IPythonの%time%timeit%prun、および%lprunline_profilerがインストールされている場合)は、プロファイリングのニーズを十分に満たします。ただし、GUIでのユーザーのマウスの動きなど、インタラクティブに駆動される計算のプロファイルを作成しようとすると、tic-tocのような機能のユースケースが発生しました。ソースでticsとtocsをスパミングすると、インタラクティブにテストすることがボトルネックを発見する最も速い方法になると感じました。私は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())

これはスタックの開始時間をプッシュすることで機能するため、ticsおよびtocsの複数のレベルで正しく機能します。また、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のような平均化戦略が忠実な測定を得るために必要です。

11
Stefan

誰かが興味を持っている場合に備えて。他のすべての答えに基づいて、私はそれらすべての中で最高のtictocクラスを書きました。

Githubのリンクは こちら です

また、pipを使用して取得することもできます。

pip install ttictoc

使用方法に関して:

インポートする

from ttictoc import TicToc

「with」ステートメントの使用

オブジェクトを作成せずに、次のようにコードの時間を調整できます。

with TicToc('name'):
  some code...

# Prints the elapsed time

または、オブジェクトを作成することで同じことができます。

t = TicToc('name')
with t:
  some code...

# Prints the elapsed time

Tic tocを明示的に呼び出す

以下に示すように、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つの引数があります。

  • name:オブジェクトの名前です。必須ではありません。
  • method:時間を取得するために使用するメソッドを示します。
  • インデント:同じオブジェクトを数回、異なるインデントで使用できます。

メソッドの引数は、int、str、または選択したメソッドのいずれかです。文字列の場合、有効な値はtime、perf_counter、およびprocess_timeです。整数の場合、有効な値は0、1、および2です。

  • timeまたは0:time.time
  • perf_counterまたは1:time.perf_counter
  • process_timeまたは2:time.process_time

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
7
H. Sánchez

ネストされたチックの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()

役に立てば幸いです。

5
antonimmo

timeit モジュールをご覧ください。実際には同等ではありませんが、時間を計りたいコードが関数内にある場合、簡単に使用できます。

3
ThiefMaster

@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
1
Shaohua Li

これは、ラッパーを使用して行うこともできます。時間を管理する非常に一般的な方法。

このサンプルコードのラッパーは、任意の関数をラップし、関数の実行に必要な時間を出力します。

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()
1
Coen Jonker

更新 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
1
Josiah Yoder

ステファンとアントニンモの答えに基づいて、私は最終的に

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は経過時間を出力しませんが、それを返します。)

0
Maxim