ฟังก์ชันการวัดเวลา Python


123

ฉันต้องการสร้างฟังก์ชัน python เพื่อทดสอบเวลาที่ใช้ในแต่ละฟังก์ชันและพิมพ์ชื่อพร้อมเวลาฉันจะพิมพ์ชื่อฟังก์ชันได้อย่างไรและมีวิธีอื่นในการทำอย่างไรโปรดบอกฉันด้วย

def measureTime(a):
    start = time.clock() 
    a()
    elapsed = time.clock()
    elapsed = elapsed - start
    print "Time spent in (function name) is: ", elapsed

เครื่องมือสร้างโปรไฟล์ Python สามารถแสดงชื่อฟังก์ชันและเวลาที่ใช้ในแต่ละฟังก์ชัน อ่านที่นี่: docs.python.org/library/profile.html
Roadmaster

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

คำตอบ:


241

แรกและสำคัญที่สุดผมขอแนะนำให้ใช้Profilerหรือการใช้ atleast timeit

อย่างไรก็ตามหากคุณต้องการเขียนวิธีการกำหนดเวลาของคุณเองอย่างเคร่งครัดเพื่อเรียนรู้นี่คือสถานที่ที่จะเริ่มต้นโดยใช้มัณฑนากร

Python 2:

def timing(f):
    def wrap(*args):
        time1 = time.time()
        ret = f(*args)
        time2 = time.time()
        print '%s function took %0.3f ms' % (f.func_name, (time2-time1)*1000.0)
        return ret
    return wrap

และการใช้งานนั้นง่ายมากเพียงใช้ @timing มัณฑนากร:

@timing
def do_work():
  #code

Python 3:

def timing(f):
    def wrap(*args, **kwargs):
        time1 = time.time()
        ret = f(*args, **kwargs)
        time2 = time.time()
        print('{:s} function took {:.3f} ms'.format(f.__name__, (time2-time1)*1000.0))

        return ret
    return wrap

หมายเหตุฉันกำลังเรียกf.func_nameชื่อฟังก์ชันเป็นสตริง (ใน Python 2) หรือf.__name__ ใน Python 3


4
สิ่งที่ฉันต้องการ :) ... แต่พวกคุณชักจูงให้ฉันใช้ python profiler
Wazery

3
ดูเหมือนว่าสิ่งนี้จะถือว่า time.time () รายงานเวลาเป็นไมโครวินาทีตั้งแต่ยุค? เอกสารกล่าวว่ารายงานเวลาในไม่กี่วินาทีdocs.python.org/2/library/time.html#time.time
Rahul Jha

สิ่งนี้จะไม่มีผลหลังจากใช้ yield ใน func ยังคงใช้วิธีนี้และสามารถใช้ผลตอบแทนได้อย่างไร?
jiamo

def เวลา (f): def ห่อ (* args, ** kwargs): time1 = time.time () ret = f (* args, ** kwargs) time2 = time.time () print '% s function ใช้เวลา% 0.3 f ms '% (f.func_name, (time2-time1) * 1000) return ret return wrap
Vivek Bagaria

1
ข้อเสียของการเขียนเองคืออะไร? การจัดเก็บรายการเวลาที่ผ่านไปและการตรวจสอบการกระจายไม่ง่ายเพียงพอหรือไม่
3pitt

51

หลังจากเล่นกับtimeitโมดูลแล้วฉันไม่ชอบอินเทอร์เฟซของมันซึ่งดูไม่ค่อยหรูหราเมื่อเทียบกับสองวิธีต่อไปนี้

โค้ดต่อไปนี้อยู่ใน Python 3

วิธีมัณฑนากร

ซึ่งเกือบจะเหมือนกันกับวิธีการของ @ Mike ที่นี่ฉันเพิ่มkwargsและfunctoolsห่อเพื่อให้ดีขึ้น

def timeit(func):
    @functools.wraps(func)
    def newfunc(*args, **kwargs):
        startTime = time.time()
        func(*args, **kwargs)
        elapsedTime = time.time() - startTime
        print('function [{}] finished in {} ms'.format(
            func.__name__, int(elapsedTime * 1000)))
    return newfunc

@timeit
def foobar():
    mike = Person()
    mike.think(30)

วิธีการจัดการบริบท

from contextlib import contextmanager

@contextmanager
def timeit_context(name):
    startTime = time.time()
    yield
    elapsedTime = time.time() - startTime
    print('[{}] finished in {} ms'.format(name, int(elapsedTime * 1000)))

ตัวอย่างเช่นคุณสามารถใช้เช่น:

with timeit_context('My profiling code'):
    mike = Person()
    mike.think()

และรหัสภายในwithบล็อกจะถูกกำหนดเวลา

ข้อสรุป

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

ตัวอย่างเช่นตอนนี้คุณมี

images = get_images()
bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)

ตอนนี้คุณต้องการเวลาbigImage = ...บรรทัด หากคุณเปลี่ยนเป็นฟังก์ชันจะเป็น:

images = get_images()
bitImage = None
@timeit
def foobar():
    nonlocal bigImage
    bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)

ดูไม่ค่อยดีนัก ... จะเป็นอย่างไรถ้าคุณอยู่ใน Python 2 ซึ่งไม่มีnonlocalคีย์เวิร์ด

แทนที่จะใช้วิธีที่สองเหมาะกับที่นี่มาก:

images = get_images()
with timeit_context('foobar'):
    bigImage = ImagePacker.pack(images, width=4096)
drawer.draw(bigImage)

การมีส่วนร่วมที่น่าสนใจ แต่ฉันคิดว่ามันไร้ประโยชน์ที่ในวิธีการตกแต่งที่คุณพูดถึงคุณต้องเปลี่ยนtimeitอินเทอร์เฟซและใช้wraps()ฟังก์ชันของfunctoolsโมดูล ฉันหมายความว่ารหัสพิเศษทั้งหมดนั้นไม่จำเป็น
Billal Begueradj

1
ความต้องการimport functools
Guillaume Chevalier

1
โปรดทราบว่ามัณฑนากรของคุณสูญเสียค่าส่งคืนของฟังก์ชันเดิม
Marc Van Daele

11

ฉันไม่เห็นว่าtimeitโมดูลมีปัญหาอะไร นี่อาจเป็นวิธีที่ง่ายที่สุดที่จะทำ

import timeit
timeit.timeit(a, number=1)

นอกจากนี้ยังสามารถส่งอาร์กิวเมนต์ไปยังฟังก์ชัน สิ่งที่คุณต้องมีคือรวมฟังก์ชันของคุณโดยใช้มัณฑนากร คำอธิบายเพิ่มเติมที่นี่: http://www.pythoncentral.io/time-a-python-function/

กรณีเดียวที่คุณอาจสนใจในการเขียนคำสั่งเวลาของคุณเองคือถ้าคุณต้องการเรียกใช้ฟังก์ชันเพียงครั้งเดียวและต้องการรับค่าส่งคืนด้วย

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


3
การส่งอาร์กิวเมนต์ไปยังฟังก์ชันโดยใช้เครื่องห่อและมัณฑนากร? ทำไมไม่timeit.timeit(lambda: func(a,b,c), number=1)? ฉันใช้สิ่งนี้เมื่อทำการทดสอบเกี่ยวกับโซลูชันสมมุติฐานในเทอร์มินัล
แจ็ค

11

Timeit มีข้อบกพร่องใหญ่สองประการ: จะไม่ส่งคืนค่าส่งคืนของฟังก์ชันและใช้ eval ซึ่งต้องส่งรหัสการตั้งค่าเพิ่มเติมสำหรับการนำเข้า วิธีนี้ช่วยแก้ปัญหาทั้งสองอย่างได้อย่างเรียบง่ายและสวยงาม:

def timed(f):
  start = time.time()
  ret = f()
  elapsed = time.time() - start
  return ret, elapsed

timed(lambda: database.foo.execute('select count(*) from source.apachelog'))
(<sqlalchemy.engine.result.ResultProxy object at 0x7fd6c20fc690>, 4.07547402381897)

ขอบคุณ! timeit ใช้ไม่ได้กับ Apache Spark เพราะคุณต้องนำเข้าการพึ่งพา Spark ทั้งหมดและใครต้องการสร้างสตริงเก่าขนาดใหญ่ที่ทำเช่นนั้น วิธีนี้ง่ายกว่าและยืดหยุ่นกว่ามาก
พอล

4

มีเครื่องมือที่ง่ายสำหรับการจับเวลา https://github.com/RalphMao/PyTimer

สามารถทำงานได้เหมือนมัณฑนากร :

from pytimer import Timer
@Timer(average=False)      
def matmul(a,b, times=100):
    for i in range(times):
        np.dot(a,b)        

เอาท์พุท:

matmul:0.368434
matmul:2.839355

นอกจากนี้ยังสามารถทำงานเหมือนตัวจับเวลาปลั๊กอินที่มีการควบคุมเนมสเปซ (มีประโยชน์หากคุณกำลังแทรกลงในฟังก์ชันที่มีรหัสจำนวนมากและอาจถูกเรียกใช้ที่อื่น)

timer = Timer()                                           
def any_function():                                       
    timer.start()                                         

    for i in range(10):                                   

        timer.reset()                                     
        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
        timer.checkpoint('block1')                        

        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
        timer.checkpoint('block2')                        
        np.dot(np.ones((100,1000)), np.zeros((1000,1000)))

    for j in range(20):                                   
        np.dot(np.ones((100,1000)), np.zeros((1000,500)))
    timer.summary()                                       

for i in range(2):                                        
    any_function()                                        

เอาท์พุท:

========Timing Summary of Default Timer========
block2:0.065062
block1:0.032529
========Timing Summary of Default Timer========
block2:0.065838
block1:0.032891

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


3

วิธีการตกแต่งโดยใช้ห้องสมุดมัณฑนากร Python:

import decorator

@decorator
def timing(func, *args, **kwargs):
    '''Function timing wrapper
        Example of using:
        ``@timing()``
    '''

    fn = '%s.%s' % (func.__module__, func.__name__)

    timer = Timer()
    with timer:
        ret = func(*args, **kwargs)

    log.info(u'%s - %0.3f sec' % (fn, timer.duration_in_seconds()))
    return ret

ดูโพสต์ในบล็อกของฉัน:

โพสต์บนบล็อก mobilepro.pl

โพสต์ของฉันบน Google Plus


1

วิธีการทำของฉัน:

from time import time

def printTime(start):
    end = time()
    duration = end - start
    if duration < 60:
        return "used: " + str(round(duration, 2)) + "s."
    else:
        mins = int(duration / 60)
        secs = round(duration % 60, 2)
        if mins < 60:
            return "used: " + str(mins) + "m " + str(secs) + "s."
        else:
            hours = int(duration / 3600)
            mins = mins % 60
            return "used: " + str(hours) + "h " + str(mins) + "m " + str(secs) + "s."

ตั้งค่าตัวแปรstart = time()ก่อนเรียกใช้ฟังก์ชัน / ลูปและprintTime(start)อยู่หลังบล็อก

และคุณได้รับคำตอบ

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