ฉันจะจับเวลาส่วนรหัสสำหรับการทดสอบประสิทธิภาพด้วย Pythons timeit ได้อย่างไร


162

ฉันเป็นสคริปต์ไพ ธ อนซึ่งทำงานได้ตามปกติ แต่ฉันต้องเขียนเวลาดำเนินการ ฉัน googled ที่ฉันควรใช้timeitแต่ฉันไม่สามารถใช้งานได้

สคริปต์ Python ของฉันมีลักษณะดังนี้:

import sys
import getopt
import timeit
import random
import os
import re
import ibm_db
import time
from string import maketrans
myfile = open("results_update.txt", "a")

for r in range(100):
    rannumber = random.randint(0, 100)

    update = "update TABLE set val = %i where MyCount >= '2010' and MyCount < '2012' and number = '250'" % rannumber
    #print rannumber

    conn = ibm_db.pconnect("dsn=myDB","usrname","secretPWD")

for r in range(5):
    print "Run %s\n" % r        
    ibm_db.execute(query_stmt)
 query_stmt = ibm_db.prepare(conn, update)

myfile.close()
ibm_db.close(conn)

results_update.txtสิ่งที่ฉันต้องการก็คือเวลาที่ใช้ในการดำเนินการค้นหาและเขียนไปยังแฟ้ม วัตถุประสงค์คือเพื่อทดสอบคำสั่งการอัพเดทสำหรับฐานข้อมูลของฉันด้วยดัชนีและกลไกการปรับแต่งที่แตกต่างกัน


คำถามของคุณเกี่ยวกับtimeitอะไร ฉันเดาว่าไม่. ในกรณีนี้คุณควรลบ "with Pythons timeit" ออกจากชื่อ
Martin Thoma

คำตอบ:


275

คุณสามารถใช้time.time()หรือtime.clock()ก่อนและหลังบล็อกที่คุณต้องการเวลา

import time

t0 = time.time()
code_block
t1 = time.time()

total = t1-t0

วิธีนี้ไม่ถูกต้องเหมือนtimeit(ไม่ใช่ค่าเฉลี่ยการรันหลายครั้ง) แต่มันตรงไปตรงมา

time.time()(ใน Windows และ Linux) และtime.clock()(ใน Linux) ไม่แม่นยำเพียงพอสำหรับฟังก์ชั่นที่รวดเร็ว (คุณจะได้รับทั้งหมด = 0) ในกรณีนี้หรือถ้าคุณต้องการจะเฉลี่ยเวลาที่ผ่านไปหลายวิ่งคุณมีการโทรด้วยตนเองฟังก์ชั่นหลายครั้ง (ในฐานะที่ฉันคิดว่าคุณทำอยู่แล้วในตัวคุณและโค้ดตัวอย่าง timeit ไม่โดยอัตโนมัติเมื่อคุณตั้งค่าของจำนวนอาร์กิวเมนต์)

import time

def myfast():
   code

n = 10000
t0 = time.time()
for i in range(n): myfast()
t1 = time.time()

total_n = t1-t0

ใน Windows เป็นคอเรย์ที่ระบุไว้ในการแสดงความคิดเห็นที่time.clock()มีความแม่นยำสูงมาก (มิลลิแทนสอง) time.time()และเป็นที่ต้องการมากกว่า


8
fyi บน windows ให้ใช้ time.clock () แทน time.time ()
Corey Goldberg

4
ขอบคุณ Corey ทำไม เพราะนาฬิกามีความแม่นยำมากขึ้น (ไมโครวินาที) หรือมีอะไรมากกว่านี้?
joaquin

11
คุณสามารถใช้ timeit.default_timer () เพื่อทำให้แพลตฟอร์มโค้ดของคุณเป็นอิสระ ส่งคืนทั้ง time.clock () หรือ time.time () ตามความเหมาะสมกับระบบปฏิบัติการ
Marc Stober

6
แทนที่จะเลือกนาฬิกาด้วยมือให้ใช้timeit.default_timer; Python ทำงานให้คุณแล้ว แต่จริงๆแล้วคุณควรใช้timeit.timeit(myfast, number=n)แทนการประดิษฐ์วงล้อโทรซ้ำ (และพลาดข้อเท็จจริงที่timeitปิดใช้งานตัวรวบรวมขยะในขณะที่เรียกใช้รหัสซ้ำ ๆ )
Martijn Pieters

15
update: time.clock () ไม่สนับสนุนในขณะนี้ คุณควรใช้ time.time () ที่จริงแล้วตั้งแต่เวอร์ชั่น 3.3 ตัวเลือกที่ดีที่สุดจะเป็น time.perf_counter ()
Madlozoz

42

หากคุณกำลัง profiling รหัสของคุณและสามารถใช้ IPython %timeitก็มีฟังก์ชั่นมายากล

%%timeit ทำงานบนเซลล์

In [2]: %timeit cos(3.14)
10000000 loops, best of 3: 160 ns per loop

In [3]: %%timeit
   ...: cos(3.14)
   ...: x = 2 + 3
   ...: 
10000000 loops, best of 3: 196 ns per loop

36

รหัสนี้ที่คุณแสดงนั้นค่อนข้างไม่ถูกต้อง: คุณทำการเชื่อมต่อ 100 ครั้ง (ไม่สนใจทั้งหมด แต่อย่างสุดท้าย) จากนั้นเมื่อคุณทำการเรียกใช้ครั้งแรกคุณจะผ่านตัวแปรโลคัลquery_stmtที่คุณเริ่มต้นหลังจากการเรียกใช้งานเท่านั้นโทร.

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

โดยเฉพาะถ้าฟังก์ชั่นที่คุณต้องการเวลาเป็นพารามิเตอร์ที่น้อยกว่าที่เรียกว่าfoobarคุณสามารถใช้timeit.timeit (2.6 หรือสูงกว่า - มันซับซ้อนกว่าใน 2.5 และก่อนหน้า):

timeit.timeit('foobar()', number=1000)

คุณควรระบุจำนวนการรันเนื่องจากค่าเริ่มต้นหนึ่งล้านอาจสูงสำหรับกรณีการใช้งานของคุณ (นำไปสู่การใช้จ่ายจำนวนมากในรหัสนี้ ;-)


26
หลังจากดิ้นรนกับสิ่งนี้ในช่วงไม่กี่นาทีที่ผ่านมาฉันต้องการให้ผู้ชมในอนาคตรู้ว่าคุณอาจต้องการผ่านตัวแปรตั้งค่าหากฟังก์ชั่นของคุณfoobarอยู่ในไฟล์หลัก เช่นนี้:timeit.timeit('foobar()','from __main__ import foobar',number=1000)
รวย

3
ใน Python 2.7.8 คุณสามารถใช้timeit.timeit( foobar, number=1000 )

9

มุ่งเน้นไปที่สิ่งหนึ่งที่เฉพาะเจาะจง Disk I / O ช้าดังนั้นฉันจะเอามันออกจากการทดสอบหากสิ่งที่คุณจะปรับแต่งคือแบบสอบถามฐานข้อมูล

และถ้าคุณต้องการเวลาในการประมวลผลฐานข้อมูลให้มองหาเครื่องมือฐานข้อมูลแทนเช่นถามแผนแบบสอบถามและทราบว่าประสิทธิภาพจะแตกต่างกันไม่เฉพาะกับแบบสอบถามที่แน่นอนและดัชนีที่คุณมี แต่ยังมีการโหลดข้อมูลด้วย คุณเก็บไว้)

ที่กล่าวว่าคุณสามารถใส่รหัสของคุณในฟังก์ชันและเรียกใช้ฟังก์ชันนั้นด้วยtimeit.timeit():

def function_to_repeat():
    # ...

duration = timeit.timeit(function_to_repeat, number=1000)

วิธีนี้จะปิดใช้งานการรวบรวมขยะเรียกใช้function_to_repeat()ฟังก์ชันซ้ำแล้วซ้ำอีกและกำหนดเวลารวมของการโทรเหล่านั้นtimeit.default_timer()ซึ่งเป็นนาฬิกาที่มีความแม่นยำที่สุดสำหรับแพลตฟอร์มเฉพาะของคุณ

คุณควรย้ายรหัสการตั้งค่าออกจากฟังก์ชั่นซ้ำ ๆ ตัวอย่างเช่นคุณควรเชื่อมต่อกับฐานข้อมูลก่อนจากนั้นให้เวลาเฉพาะแบบสอบถาม ใช้setupอาร์กิวเมนต์เพื่อนำเข้าหรือสร้างการอ้างอิงเหล่านั้นและส่งผ่านไปยังฟังก์ชันของคุณ:

def function_to_repeat(var1, var2):
    # ...

duration = timeit.timeit(
    'function_to_repeat(var1, var2)',
    'from __main__ import function_to_repeat, var1, var2', 
    number=1000)

จะคว้า Globals function_to_repeat, var1และvar2จากสคริปต์และผ่านเหล่านั้นไปยังฟังก์ชั่นในแต่ละซ้ำ


การใส่รหัสลงในฟังก์ชั่นเป็นขั้นตอนที่ฉันกำลังมองหา - เพียงแค่การสร้างโค้ดสตริงและevalไอเอ็นจีจะไม่บินสำหรับสิ่งที่ไม่น่ารำคาญอย่างสมบูรณ์ ขอบคุณ
javadba

2

ฉันเห็นคำถามที่ได้รับคำตอบแล้ว แต่ยังต้องการเพิ่ม 2 เซนต์ของฉันเหมือนกัน

ฉันยังต้องเผชิญกับสถานการณ์ที่คล้ายกันซึ่งฉันต้องทดสอบเวลาดำเนินการสำหรับหลายวิธีและด้วยเหตุนี้จึงเขียนสคริปต์ขนาดเล็กซึ่งเรียก timeit ในฟังก์ชั่นทั้งหมดที่เขียนในมัน

สคริปต์นี้ยังมีให้เห็นในส่วนของ github ที่นี่ที่นี่

หวังว่ามันจะช่วยคุณและคนอื่น ๆ

from random import random
import types

def list_without_comprehension():
    l = []
    for i in xrange(1000):
        l.append(int(random()*100 % 100))
    return l

def list_with_comprehension():
    # 1K random numbers between 0 to 100
    l = [int(random()*100 % 100) for _ in xrange(1000)]
    return l


# operations on list_without_comprehension
def sort_list_without_comprehension():
    list_without_comprehension().sort()

def reverse_sort_list_without_comprehension():
    list_without_comprehension().sort(reverse=True)

def sorted_list_without_comprehension():
    sorted(list_without_comprehension())


# operations on list_with_comprehension
def sort_list_with_comprehension():
    list_with_comprehension().sort()

def reverse_sort_list_with_comprehension():
    list_with_comprehension().sort(reverse=True)

def sorted_list_with_comprehension():
    sorted(list_with_comprehension())


def main():
    objs = globals()
    funcs = []
    f = open("timeit_demo.sh", "w+")

    for objname in objs:
        if objname != 'main' and type(objs[objname]) == types.FunctionType:
            funcs.append(objname)
    funcs.sort()
    for func in funcs:
        f.write('''echo "Timing: %(funcname)s"
python -m timeit "import timeit_demo; timeit_demo.%(funcname)s();"\n\n
echo "------------------------------------------------------------"
''' % dict(
                funcname = func,
                )
            )

    f.close()

if __name__ == "__main__":
    main()

    from os import system

    #Works only for *nix platforms
    system("/bin/bash timeit_demo.sh")

    #un-comment below for windows
    #system("cmd timeit_demo.sh")

2

นี่คือเสื้อคลุมเรียบง่ายสำหรับคำตอบของสตีเว่น ฟังก์ชั่นนี้ไม่ได้ทำการรันซ้ำ / โดยเฉลี่ยเพียงช่วยให้คุณไม่ต้องทำซ้ำรหัสเวลาทุกที่ :)

'''function which prints the wall time it takes to execute the given command'''
def time_func(func, *args): #*args can take 0 or more 
  import time
  start_time = time.time()
  func(*args)
  end_time = time.time()
  print("it took this long to run: {}".format(end_time-start_time))

0

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

เอกสารสำหรับtimeitข้อเสนอตัวอย่างมากมายและธงมูลค่าการตรวจสอบ การใช้งานพื้นฐานบนบรรทัดคำสั่งคือ:

$ python -mtimeit "all(True for _ in range(1000))"
2000 loops, best of 5: 161 usec per loop
$ python -mtimeit "all([True for _ in range(1000)])"
2000 loops, best of 5: 116 usec per loop

เรียกใช้ด้วย-hเพื่อดูตัวเลือกทั้งหมด Python MOTWมีส่วนที่ยอดเยี่ยมtimeitที่แสดงวิธีการเรียกใช้โมดูลผ่านการอิมพอร์ตและสตริงโค้ดหลายบรรทัดจากบรรทัดคำสั่ง

ในรูปแบบสคริปต์ฉันมักจะใช้มันเช่นนี้:

import argparse
import copy
import dis
import inspect
import random
import sys
import timeit

def test_slice(L):
    L[:]

def test_copy(L):
    L.copy()

def test_deepcopy(L):
    copy.deepcopy(L)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--n", type=int, default=10 ** 5)
    parser.add_argument("--trials", type=int, default=100)
    parser.add_argument("--dis", action="store_true")
    args = parser.parse_args()
    n = args.n
    trials = args.trials
    namespace = dict(L = random.sample(range(n), k=n))
    funcs_to_test = [x for x in locals().values() 
                     if callable(x) and x.__module__ == __name__]
    print(f"{'-' * 30}\nn = {n}, {trials} trials\n{'-' * 30}\n")

    for func in funcs_to_test:
        fname = func.__name__
        fargs = ", ".join(inspect.signature(func).parameters)
        stmt = f"{fname}({fargs})"
        setup = f"from __main__ import {fname}"
        time = timeit.timeit(stmt, setup, number=trials, globals=namespace)
        print(inspect.getsource(globals().get(fname)))

        if args.dis:
            dis.dis(globals().get(fname))

        print(f"time (s) => {time}\n{'-' * 30}\n")

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

ตัวอย่างผลลัพธ์:

$ python benchmark.py --n 10000
------------------------------
n = 10000, 100 trials
------------------------------

def test_slice(L):
    L[:]

time (s) => 0.015502399999999972
------------------------------

def test_copy(L):
    L.copy()

time (s) => 0.01651419999999998
------------------------------

def test_deepcopy(L):
    copy.deepcopy(L)

time (s) => 2.136012
------------------------------

0

อีกตัวอย่างง่ายๆ timeit:

def your_function_to_test():
   # do some stuff...

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