Pythons timeit ile performansı test etmek için bir kod segmentini nasıl zamanlayabilirim?


162

Tam olması gerektiği gibi çalışan bir python betiğim var, ama yürütme süresini yazmam gerekiyor. Kullanmalıyım googledtimeit ancak işe yaramayacak gibi görünmüyorum.

Python betiğim şöyle:

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)

Ne gerek sorguyu yürütmek ve dosyaya yazmak için gereken zamandır results_update.txt. Amaç, veritabanım için bir güncelleme ifadesini farklı dizinler ve ayar mekanizmaları ile test etmektir.


Sorunuz belirli miydi / spesifik miydi timeit? Sanırım hayır. Bu durumda, muhtemelen "with Pythons timeit" başlıktan kaldırmalısınız.
Martin Thoma

Yanıtlar:


276

Sen kullanabilir time.time()veya time.clock()öncesinde ve zaman isteyen bloktan sonra.

import time

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

total = t1-t0

Bu yöntem kadar kesin değildir timeit(birkaç çalışmayı ortalama yapmaz), ancak açıktır.

time.time()(Windows ve Linux'ta) ve time.clock()(Linux'ta) hızlı işlevler için yeterince kesin değildir (toplam = 0 elde edersiniz). Bu durumda veya birkaç çalıştırmada geçen süreyi ortalamak istiyorsanız, işlevi birden çok kez manuel olarak çağırmanız gerekir (Sanırım zaten yaptığınız gibi örnek kod ve timeit, sayı bağımsız değişkenini ayarladığınızda otomatik olarak yapar )

import time

def myfast():
   code

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

total_n = t1-t0

Windows'da, Corey yorumda belirtildiği gibi time.clock(), çok daha yüksek bir hassasiyete sahiptir (ikinci yerine mikrosaniye) ve tercih edilir time.time().


8
Windows'da fyi, time.time () yerine time.clock () kullanın
Corey Goldberg

4
Teşekkürler Corey, neden? çünkü saat daha hassas (mikrosaniye) veya daha fazlası var mı?
joaquin

11
Kod platformunuzu bağımsız hale getirmek için timeit.default_timer () yöntemini kullanabilirsiniz; OS için uygun şekilde time.clock () veya time.time () değerini döndürür.
Marc Stober

6
Elle bir saat seçmek yerine timeit.default_timer; Python işi sizin için zaten yaptı. Ama gerçekten, timeit.timeit(myfast, number=n)tekrarlayan çağrı tekerleğini yeniden icat etmek yerine kullanmalısınız (ve timeitkodu tekrar tekrar çalıştırırken çöp toplayıcıyı devre dışı bırakan gerçeğini kaçırmayın ).
Martijn Pieters

15
update: time.clock () artık kullanımdan kaldırıldı. Şimdi time.time () öğesini kullanmalısınız. Aslında, 3.3 sürümünden beri, en iyi seçenek time.perf_counter ()
Madlozoz

42

Kodunuzu oluşturuyorsanız ve IPython'u kullanabiliyorsanız, sihir işlevi vardır %timeit.

%%timeit hücreler üzerinde çalışır.

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

Zamanlamanın dışında, gösterdiğiniz bu kod basitçe yanlış: 100 bağlantı yürütüyorsunuz (sonuncusu hariç tamamen yok sayılıyor) ve ardından ilk yürütme çağrısını query_stmtyaptığınızda, yalnızca yürütme işleminden sonra başlattığınız yerel bir değişkeni iletiyorsunuz. aramak.

İlk olarak, henüz zamanlama konusunda endişelenmeden kodunuzu düzeltin: örn. Bağlantı yapan veya alan ve 100 veya 500 veya bu bağlantıda istediğiniz sayıda güncelleme gerçekleştiren bir işlev, daha sonra bağlantıyı kapatır. Kodunuz doğru bir şekilde çalıştıktan sonra, üzerinde kullanmayı düşünmek için doğru nokta timeit!

Özellikle, zaman istediğiniz işlevi ise bir parametre daha az bir adlandırılan foobarkullanabileceğiniz timeit.timeit (2.6 veya sonrası - bu daha 2.5 karmaşık ve daha önce var):

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

Çalışma sayısını belirtmeniz daha iyi olur, çünkü varsayılan, bir milyon, kullanım durumunuz için yüksek olabilir (bu kodda çok fazla zaman geçirmenize neden olur ;-).


26
Son birkaç dakika için bununla mücadele ettikten sonra, gelecekteki izleyicilere işleviniz foobarbir ana dosyadaysa muhtemelen bir kurulum değişkeni iletmek istediğinizi bildirmek istiyorum . Şunun gibi: timeit.timeit('foobar()','from __main__ import foobar',number=1000)
Zengin

3
Python timeit.timeit( foobar, number=1000 )

9

Belirli bir şeye odaklanın . Disk G / Ç yavaş, bu yüzden tüm tweak veritabanı sorgusu ise testten çıkarmak istiyorum.

Veritabanı yürütmenizi zamanlamanız gerekiyorsa, bunun yerine sorgu planını sormak gibi veritabanı araçlarını arayın ve performansın yalnızca tam sorguya ve sahip olduğunuz dizinlere değil, aynı zamanda veri yüküne (ne kadar veri) bağlı olduğunu unutmayın. sakladınız).

Bununla birlikte, kodunuzu bir işleve koyabilir ve bu işlevi aşağıdakilerle çalıştırabilirsiniz timeit.timeit():

def function_to_repeat():
    # ...

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

Bu, çöp toplamayı devre dışı bırakacak, function_to_repeat()işlevi tekrar tekrar çağıracak ve timeit.default_timer()belirli aramalarınız için mevcut olan en doğru saat olan bu çağrıların toplam süresini zamanlayacaktır.

Kurulum kodunu tekrarlanan işlevden çıkarmalısınız ; örneğin, önce veritabanına bağlanmalı, sonra yalnızca sorguları zamanlamalısınız. setupBu bağımlılıkları içe aktarmak veya oluşturmak için bağımsız değişkeni kullanın ve bunları işlevinize iletin:

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

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

globalsi kapmak istiyorum function_to_repeat, var1ve var2Betiğiniz ve işlevi her tekrarı için bu geçmektedir.


Kodu bir işleve koymak için aradığım bir adım - basitçe kod bir dize yapmak ve evaling tamamen önemsiz bir şey için uçmayacak çünkü. thx
javadba

2

Sorunun zaten cevaplandığını görüyorum, ancak yine de 2 sentimi aynı şekilde eklemek istiyorum.

Ben de birkaç yaklaşım için yürütme sürelerini test etmek ve böylece içinde yazılı tüm fonksiyonlar üzerinde timeit çağıran küçük bir komut dosyası yazmak zorunda benzer senaryo ile karşı karşıya.

Komut ayrıca github ana fikri olarak kullanılabilir burada .

Size ve başkalarına yardımcı olacağını umuyoruz.

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

İşte Steven'ın cevabı için basit bir sarıcı. Bu işlev tekrarlanan çalışma / ortalama yapmaz, sadece zamanlama kodunu her yerde tekrarlamak zorunda kalmazsınız :)

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

Test paketi içe aktarılanı kullanma girişiminde bulunmaz, timeitbu nedenle amacın ne olduğunu söylemek zordur. Bununla birlikte, bu kanonik bir cevaptır, bu yüzden Martijn'ın cevabını timeitayrıntılı olarak anlatan , tam bir örnek görünüyor .

İçin dokümanlartimeit teklif birçok örnek ve bayraklar değerinde kontrol ediyor. Komut satırındaki temel kullanım:

$ 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

-hTüm seçenekleri görmek için ile çalıştırın . Python MOTW , timeitmodüllerin komut satırından içe aktarma ve çok satırlı kod dizeleri aracılığıyla nasıl çalıştırılacağını gösteren harika bir bölüme sahiptir .

Komut dosyası formunda, genellikle şöyle kullanırım:

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

İhtiyacınız olan fonksiyonları ve argümanları kolayca bırakabilirsiniz. Saf olmayan fonksiyonları kullanırken dikkatli olun ve durumla ilgilenin.

Örnek çıktı:

$ 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

Başka bir basit timeit örneği:

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

time_to_run_100_times = timeit.timeit(lambda: your_function_to_test, number=100)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.