Python เทียบเท่ากับฟังก์ชัน tic และ toc ของ Matlab อย่างไร


113

Python เทียบเท่ากับฟังก์ชัน tic และ tocของ Matlab อย่างไร


8
ถ้าคุณต้องการให้เทียบเท่าโดยตรงจริงๆเพียงโทรtic = time.time()และtoc = time.time()แล้วprint toc-tic, 'sec Elapsed'ในฐานะที่เป็นคนได้กล่าวว่าด้านล่าง แต่timeitมีประสิทธิภาพมากขึ้น
Joe Kington

ฉันดูเหมือนจะได้รับผลดีกว่าการใช้วิธีการ @ JoeKington ในร่วมกับ timeit.default_timer () เช่นนี้ตัวอย่างเช่น: แล้วtic = timeit.default_timer(); (U,S,V) = np.linalg.svd(A); toc = timeit.default_timer() print toc-tic
littleO

1
pytictoc ของไลบรารีดูเหมือนจะสื่อความหมายได้มากที่สุดไวยากรณ์ยังค่อนข้างเรียบง่ายกว่า ttictoc ด้านล่าง pypi.org/project/pytictoc
FlorianH

คำตอบ:


174

นอกเหนือจากtimeitที่ ThiefMaster กล่าวถึงแล้ววิธีง่ายๆในการทำก็คือ (หลังจากนำเข้า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- ทั้งหมดขึ้นอยู่กับสิ่งที่คุณต้องการวัด


25
@eat: ฉันไม่เห็นด้วยอย่างเคารพ ผู้คนใช้timeคำสั่งunix เพื่อวัดเวลาทำงานของโปรแกรมมาโดยตลอดและวิธีนี้จะจำลองสิ่งนี้ภายในรหัส Python ฉันไม่เห็นอะไรผิดปกติตราบใดที่มันเป็นเครื่องมือที่เหมาะสมสำหรับงาน timeitไม่ได้เป็นเช่นนั้นเสมอไปและผู้สร้างโปรไฟล์เป็นโซลูชันที่มีน้ำหนักมากสำหรับความต้องการส่วนใหญ่
Eli Bendersky

4
print 'Elapsed: %.2f seconds % (time.time() - self.tstart)'สำหรับบรรทัดสุดท้ายที่ผมจะแนะนำ มันยากที่จะเข้าใจหากไม่มี% .2f ขอบคุณสำหรับความคิดที่ดี
Can Kavaklıoğlu

4
สิ่งนี้ดูสะดวกมากในตอนแรก แต่ในทางปฏิบัติต้องใช้หนึ่งในการเยื้องบล็อกโค้ดที่ต้องการเวลาซึ่งอาจไม่สะดวกขึ้นอยู่กับความยาวของบล็อกโค้ดและตัวแก้ไขที่เลือก ยังคงเป็นวิธีการแก้ปัญหาที่สวยงามซึ่งทำงานได้อย่างถูกต้องในกรณีของการใช้งานแบบซ้อนกัน
Stefan

1
ฉันคิดว่าคุณต้องการแทนelapsed = t - time.time() elapsed = time.time() - tในช่วงหลังที่ผ่านไปจะเป็นลบ ฉันแนะนำการเปลี่ยนแปลงนี้เป็นการแก้ไข
rysqui

3
@rysqui - เวลาปัจจุบันไม่ใช่ตัวเลขที่มากกว่าครั้งก่อนเสมอไปใช่หรือไม่? ฉันคิดว่านั่นelapsed = time.time() - tเป็นรูปแบบที่ให้ค่าเป็นบวกเสมอ
Scott Smith

32

ฉันมีคำถามเดียวกันเมื่อฉันย้ายไป python จาก Matlab ด้วยความช่วยเหลือของเธรดนี้ฉันสามารถสร้างอะนาล็อกที่แน่นอนของ Matlab tic()และtoc()ฟังก์ชันได้ เพียงใส่รหัสต่อไปนี้ที่ด้านบนของสคริปต์ของคุณ

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)

แค่นั้นแหละ! ตอนนี้เราพร้อมที่จะใช้งานอย่างเต็มที่tic()และtoc()เช่นเดียวกับใน Matlab ตัวอย่างเช่น

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)

ตอนนี้คุณควรแบ่งเวลาได้สองอย่าง: ในตัวอย่างต่อไปนี้เรากำหนดเวลาสคริปต์ทั้งหมดและบางส่วนของสคริปต์แยกกัน

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

ฉันหวังว่านี่จะเป็นประโยชน์


22

อะนาล็อกที่ดีที่สุดของ 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()

6
สิ่งนี้จะทำงานไม่ถูกต้องในกรณีของการใช้งานแบบซ้อนticและtocซึ่ง Matlab รองรับ ต้องใช้ความซับซ้อนมากขึ้นเล็กน้อย
Stefan

2
ฉันได้ใช้ฟังก์ชันที่คล้ายกันในโค้ดของฉันเองเมื่อฉันต้องการเวลาพื้นฐาน อย่างไรก็ตามฉันจะลบimport timeด้านนอกของทั้งสองฟังก์ชั่นเนื่องจากอาจใช้เวลาพอสมควร
Bas Swinckels

หากคุณยืนยันที่จะใช้เทคนิคนี้และคุณต้องการใช้เพื่อจัดการ tic / toc ที่ซ้อนกันให้สร้างรายการทั่วโลกจากนั้นให้ticกดไปที่มันและtocเปิดจากมัน
Ahmed Fasih

1
นอกจากนี้ฉันอ่านที่อื่นที่timeit.default_timer()ดีกว่าtime.time()เพราะtime.clock()อาจเหมาะสมกว่าขึ้นอยู่กับระบบปฏิบัติการ
มิเกล

@ AhmedFasih นั่นคือสิ่งที่คำตอบของฉันแม้ว่าจะมีอะไรให้ปรับปรุงมากกว่านี้
antonimmo

15

โดยปกติ IPython ของ%time, %timeit, %prunและ%lprun(ถ้าใครได้line_profilerติดตั้ง) ตอบสนองความต้องการโปรไฟล์ของฉันค่อนข้างดี อย่างไรก็ตามกรณีการใช้งานสำหรับtic-tocฟังก์ชัน -like เกิดขึ้นเมื่อฉันพยายามสร้างโปรไฟล์การคำนวณที่ขับเคลื่อนแบบโต้ตอบกล่าวคือโดยการเคลื่อนไหวเมาส์ของผู้ใช้ใน GUI ฉันรู้สึกเหมือนสแปมticและtocในแหล่งที่มาโต้ตอบขณะที่การทดสอบจะเป็นวิธีที่เร็วที่สุดที่จะเผยให้เห็นคอขวด ฉันไปกับTimerชั้นเรียนของ Eli Bendersky แต่ไม่มีความสุขอย่างเต็มที่เนื่องจากต้องเปลี่ยนการเยื้องรหัสของฉันซึ่งอาจไม่สะดวกในผู้แก้ไขบางคนและทำให้ระบบควบคุมเวอร์ชันสับสน ยิ่งไปกว่านั้นอาจจำเป็นต้องวัดเวลาระหว่างจุดต่างๆในฟังก์ชั่นต่างๆซึ่งจะใช้ไม่ได้กับไฟล์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คำสั่งเพื่อแสดงข้อมูลเพิ่มเติมซึ่งฉันชอบเกี่ยวกับTimerคลาสของ Eli

ด้วยเหตุผลบางอย่างฉันกังวลกับค่าใช้จ่ายของการใช้งาน 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จำเป็นต้องมีกลยุทธ์การหาค่าเฉลี่ยเช่น in เพื่อให้ได้การวัดที่น่าเชื่อถือ


1
สง่างามมาก @Stefan - ไม่อยากจะเชื่อเลยว่านี่เป็นคะแนนที่ต่ำมาก ขอบคุณ!
thclark

10

คุณสามารถใช้ticและจากtoc ttictocติดตั้งด้วย

pip install ttictoc

และนำเข้าในสคริปต์ของคุณดังต่อไปนี้

from ttictoc import tic,toc
tic()
# Some code
print(toc())

8

ฉันเพิ่งสร้างโมดูล [tictoc.py] สำหรับการบรรลุ tic tocs ที่ซ้อนกันซึ่งเป็นสิ่งที่ 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()

ฉันหวังว่ามันจะช่วยได้


การจำลอง tic / toc ที่ดีจาก MATLAB!
Matt

1
ฉันต้องเตือนคุณว่าสิ่งนี้อาจไม่ทำงานตามที่ต้องการเมื่อใช้พร้อมกันมากกว่า 1 โมดูลเนื่องจากโมดูล (AFAIK) ทำงานเหมือน singletons
antonimmo

3

ดูที่timeitโมดูล มันไม่เทียบเท่าจริงๆ แต่ถ้ารหัสที่คุณต้องการเวลาอยู่ในฟังก์ชันคุณสามารถใช้งานได้อย่างง่ายดาย


ใช่timeitเหมาะสำหรับเกณฑ์มาตรฐาน ไม่จำเป็นต้องเป็นฟังก์ชันเดียวคุณสามารถส่งผ่านคำสั่งที่ซับซ้อนอย่างผิดปกติได้

10
การส่งรหัสที่ไม่ใช่การเรียกใช้ฟังก์ชันธรรมดาอย่างสตริงนั้นน่าเกลียดมาก
ThiefMaster

2
pip install easy-tictoc

ในรหัส:

from tictoc import tic, toc

tic()

#Some code

toc()

ข้อจำกัดความรับผิดชอบ: ฉันเป็นผู้เขียนห้องสมุดนี้


โปรดอย่าคัดลอกคำตอบอื่นแม้ว่าจะเป็นคำตอบของคุณเองก็ตาม stackoverflow.com/a/59271862/8239061
SecretAgentMan

1

สามารถทำได้โดยใช้กระดาษห่อหุ้ม วิธีการรักษาเวลาโดยทั่วไป

Wrapper ในโค้ดตัวอย่างนี้จะปิดฟังก์ชันใด ๆ และพิมพ์ระยะเวลาที่จำเป็นในการเรียกใช้ฟังก์ชัน:

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

ฟังก์ชัน wrapper เวลานี้เรียกว่ามัณฑนากร อ่านรายละเอียดเพิ่มเติมได้ที่นี่ medium.com/pythonhive/…
Mircea

1

ฉันเปลี่ยนคำตอบของ @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

3
ปัญหาในการใช้งานนี้คือข้อเท็จจริงซึ่งtimerจะไม่ถูกลบออกหลังจากการโทรครั้งสุดท้ายหากมีรหัสอื่น ๆ ตามหลังการforวนซ้ำ จะได้รับค่าตัวจับเวลาสุดท้ายหนึ่งควรลบหรือเขียนทับtimerหลังจากที่วงเช่นผ่านทางfor timer = None
bastelflp

1
@bastelflp เพิ่งรู้ว่าฉันเข้าใจผิดว่าคุณหมายถึงอะไร ... ตอนนี้คำแนะนำของคุณได้รวมอยู่ในโค้ดแล้ว ขอบคุณ.
Shaohua Li

1

การอัปเดตคำตอบของ Eliสำหรับ 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 แนะนำและมีตัวเลือกในการต่อท้ายไฟล์บันทึกด้วย คุณต้องนำเข้าวันที่และเวลาเพื่อใช้คุณลักษณะการบันทึก:

import time
import datetime 
with Timer('Count', 'log.txt'):    
    for i in range(0,10_000_000):
        pass

0

จากคำตอบของ Stefan และ antonimmo ฉันลงเอยด้วยการวาง

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 ไม่พิมพ์เวลาที่ผ่านไป แต่ส่งกลับ)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.