Timeit modülü nasıl kullanılır


351

Ne timeitişe yaradığını anlıyorum ama koduma nasıl uygulayacağından emin değilim.

Söyle insertion_sortve tim_sortile iki işlevi nasıl karşılaştırabilirim timeit?

Yanıtlar:


266

Yolu sürümüyle gelen timeit çalışır kez kurulum kod çalıştırmasına ve sonra tabloların bir dizi tekrarlanan çağrılar yapmaktır. Bu nedenle, sıralamayı test etmek istiyorsanız, yerinde sıralamada bir geçişin zaten sıralanmış verilerle bir sonraki geçişi etkilememesi için biraz dikkat gerekir (elbette, Timsort'un gerçekten en iyi performansı gösterdiği için parlamasını sağlar) veriler kısmen sipariş edildiğinde).

Sıralama için bir testin nasıl oluşturulacağına dair bir örnek:

>>> import timeit

>>> setup = '''
import random

random.seed('slartibartfast')
s = [random.random() for i in range(1000)]
timsort = list.sort
'''

>>> print min(timeit.Timer('a=s[:]; timsort(a)', setup=setup).repeat(7, 1000))
0.334147930145

İfadeler dizisinin, her geçişte sıralanmamış verilerin yeni bir kopyasını oluşturduğunu unutmayın.

Ayrıca, ölçüm paketini yedi kez çalıştırma ve yalnızca en iyi zamanı tutma zamanlama tekniğine dikkat edin - bu, sisteminizde çalışan diğer işlemler nedeniyle ölçüm bozulmalarını azaltmaya gerçekten yardımcı olabilir.

Bunlar timeit'i doğru kullanmak için ipuçları. Bu yardımcı olur umarım :-)


8
Evet, liste kopyasını içerir (sıralamanın kendisine göre çok hızlıdır). Yine de kopyalama yapmazsanız, ilk geçiş listeyi sıralar ve kalan geçiş herhangi bir iş yapmak zorunda değildir. Sadece sıralama için zamanı bilmek istiyorsanız, o zaman ve olmadan yukarı çalıştırın timsort(a)ve farkı alın :-)
Raymond Hettinger

Her kurulum için 7 kez tekrar ve sonra ortalama tavsiye ederim; tersi değil. Bu şekilde, diğer süreçlerden kaynaklanan her ani yükselişin ortalaması alınmak yerine tamamen göz ardı edilme şansı varsa.
max

75
@max Zamanlamaların ortalaması yerine min () kullanın. Bu benden, Tim Peters'tan ve Guido van Rossum'dan bir tavsiye. En hızlı süre, bir algoritmanın önbellekler yüklendiğinde ve sistem diğer görevlerle meşgul olmadığında gerçekleştirebileceği en iyi zamanı gösterir. Tüm zamanlamalar gürültülü - en hızlı zaman en az gürültülü. En hızlı zamanlamaların en çok tekrarlanabilir olduğunu ve bu nedenle iki farklı uygulamanın zamanlanmasında en yararlı olduğunu göstermek kolaydır.
Raymond Hettinger

4
1000 giriş için bir ortalama (iyi, toplam, ama eşdeğer) hesaplarsınız ; sonra 7 kez tekrarlayın ve en azını alın . Ortalama (en iyi durumda değil) algoritma karmaşıklığını istediğiniz için ortalama 1000'den fazla girişe ihtiyacınız var. Kesin olarak verdiğiniz sebep için minimum değere ihtiyacınız var. Bir girdi seçerek, algoritmayı 7 kez çalıştırarak, minimum değeri alarak yaklaşımınızı geliştirebileceğimi düşündüm; daha sonra 1000 farklı girdi için tekrarlayın ve ortalamayı alın. Fark etmediğim şey, .repeat(7,1000)zaten bunu yapman (aynı tohumu kullanarak)! Yani çözümünüz mükemmel IMO.
en fazla

5
Sadece 7000 yürütme bütçenizi (ör. .repeat(7, 1000)Vs .repeat(2, 3500)vs .repeat(35, 200) nasıl ayırdığınızın, sistem yükünden kaynaklanan hatanın giriş değişkenliği nedeniyle hatayla nasıl karşılaştırıldığına bağlı olması gerektiğini ekleyebilirim. Olağanüstü bir durumda sistem ağır yük altında her zaman olur ve yürütme zamanı dağılımı (bir nadir boşta çalışma durumunda yakalamak olduğunda) ın solunda uzun ince kuyruk görürseniz, hatta bulabilir .repeat(7000,1)daha yararlı olması için .repeat(7,1000)eğer 7000'den fazla çalışmayı bütçeleyemez.
en fazla

277

timeitEtkileşimli bir Python oturumunda kullanmak istiyorsanız , iki uygun seçenek vardır:

  1. IPython kabuğunu kullanın . Uygun %timeitözel fonksiyona sahiptir:

    In [1]: def f(x):
       ...:     return x*x
       ...: 
    
    In [2]: %timeit for x in range(100): f(x)
    100000 loops, best of 3: 20.3 us per loop
    
  2. Standart bir Python yorumlayıcısında, etkileşimli oturum sırasında daha önce tanımladığınız işlevlere ve diğer adlara __main__kurulum deyiminden aktararak erişebilirsiniz :

    >>> def f(x):
    ...     return x * x 
    ... 
    >>> import timeit
    >>> timeit.repeat("for x in range(100): f(x)", "from __main__ import f",
                      number=100000)
    [2.0640320777893066, 2.0876040458679199, 2.0520210266113281]
    

97
from __main__ import fTekniği göstermek için +1 . Bunun olması gerektiği kadar yaygın olduğunu sanmıyorum. Bir işlev veya yöntem çağrısının zamanlandığı bu gibi durumlarda yararlıdır. Diğer durumlarda (bir dizi adım zamanlama), işlev çağrısı ek yükü tanıdığı için daha az yardımcı olur.
Raymond Hettinger

15
Yapabilirsiniz%timeit f(x)
qed

Not: "import f" kurulumu, hızlı normal okumaya erişim sağlar - bu, normal normal kodda genel bir işlev çağrısını (kısa hızlı işlev) tam olarak yansıtmaz. Py3.5 + 'da gerçek globaller sağlanabilir: "Sürüm 3.5'te değişti: İsteğe bağlı globals parametresi eklendi."; Kaçınılmaz olan timeit modülünün küresellerinden önce (bu çok mantıklı değil). Muhtemelen arama kodunun ( sys._getframe(N).f_globals) globalleri başlangıçtan itibaren varsayılan olmalıdır.
kxr

140

Size bir sır vereyim: Kullanmanın en iyi yolu timeitkomut satırında.

Komut satırında timeituygun istatistiksel analiz yapar: en kısa sürenin ne kadar sürdüğünü gösterir. Bu iyidir çünkü zamanlamadaki tüm hatalar pozitiftir. Yani en kısa zamanın içinde en az hata vardır. Negatif hata almanın bir yolu yoktur, çünkü bir bilgisayar hesaplayabildiğinden daha hızlı hesaplayamaz!

Yani, komut satırı arayüzü:

%~> python -m timeit "1 + 2"
10000000 loops, best of 3: 0.0468 usec per loop

Bu oldukça basit, ha?

Bir şeyler ayarlayabilirsiniz:

%~> python -m timeit -s "x = range(10000)" "sum(x)"
1000 loops, best of 3: 543 usec per loop

ki bu da faydalı!

Birden çok satır istiyorsanız, kabuğun otomatik devamını kullanabilir veya ayrı bağımsız değişkenler kullanabilirsiniz:

%~> python -m timeit -s "x = range(10000)" -s "y = range(100)" "sum(x)" "min(y)"
1000 loops, best of 3: 554 usec per loop

Bu bir kurulum verir

x = range(1000)
y = range(100)

ve zamanlar

sum(x)
min(y)

Daha uzun komut dosyalarına sahip olmak istiyorsanız, timeitbir Python komut dosyasının içine taşınmak isteyebilirsiniz . Bundan kaçınmayı öneririm çünkü analiz ve zamanlama komut satırında daha iyidir. Bunun yerine, kabuk komut dosyaları oluşturma eğilimindeyim:

 SETUP="

 ... # lots of stuff

 "

 echo Minmod arr1
 python -m timeit -s "$SETUP" "Minmod(arr1)"

 echo pure_minmod arr1
 python -m timeit -s "$SETUP" "pure_minmod(arr1)"

 echo better_minmod arr1
 python -m timeit -s "$SETUP" "better_minmod(arr1)"

 ... etc

Bu, çoklu başlatmalar nedeniyle biraz daha uzun sürebilir, ancak normalde bu büyük bir sorun değildir.


Peki ya modülünüzün içinde kullanmak isterseniztimeit ?

Basit bir yol:

def function(...):
    ...

timeit.Timer(function).timeit(number=NUMBER)

ve bu size bu sayıyı çalıştırmak için kümülatif ( minimum değil !) zaman verir.

İyi bir analiz elde etmek için .repeat, minimum değeri kullanın ve alın:

min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))

Normalde bunu, ek yükü azaltmak functools.partialyerine ile birleştirmelisiniz lambda: .... Böylece şöyle bir şey olabilir:

from functools import partial

def to_time(items):
    ...

test_items = [1, 2, 3] * 100
times = timeit.Timer(partial(to_time, test_items)).repeat(3, 1000)

# Divide by the number of repeats
time_taken = min(times) / 1000

Ayrıca şunları da yapabilirsiniz:

timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)

bu da komut satırından arabirime daha yakın bir şey verir , ancak çok daha az serin bir şekilde. "from __main__ import ..."Eğer yarattığı yapay çevre içindeki ana modülden kodu kullanmanızı sağlar timeit.

Bunun kolaylık sağlayan bir paket olduğunu Timer(...).timeit(...)ve bu nedenle zamanlamada özellikle iyi olmadığını belirtmek gerekir. Ben şahsen Timer(...).repeat(...)yukarıda gösterdiğim gibi kullanmayı tercih ederim .


Uyarılar

Her timeityerde bu tutulan birkaç uyarı var.

  • Genel gider muhasebeleştirilmez. x += 1Eklemenin ne kadar sürdüğünü öğrenmek için zaman ayırmak istediğinizi varsayalım:

    >>> python -m timeit -s "x = 0" "x += 1"
    10000000 loops, best of 3: 0.0476 usec per loop

    Peki, 0.0476 µs değil . Sadece bundan daha az olduğunu biliyorsun . Tüm hatalar olumlu.

    Bu yüzden saf yükü bulmaya çalışın :

    >>> python -m timeit -s "x = 0" ""      
    100000000 loops, best of 3: 0.014 usec per loop

    Bu sadece zamanlamadan % 30 iyi bir yük! Bu göreceli zamanlamaları büyük ölçüde eğebilir. Ama sadece ekleme zamanlarını gerçekten önemsiyordunuz ; için arama zamanlamalarının xek yüke dahil edilmesi gerekir:

    >>> python -m timeit -s "x = 0" "x"
    100000000 loops, best of 3: 0.0166 usec per loop

    Fark çok daha büyük değil, ama orada.

  • Mutasyon yöntemleri tehlikelidir.

    >>> python -m timeit -s "x = [0]*100000" "while x: x.pop()"
    10000000 loops, best of 3: 0.0436 usec per loop

    Ama bu tamamen yanlış! xilk yinelemeden sonraki boş listedir. Yeniden başlatmanız gerekecek:

    >>> python -m timeit "x = [0]*100000" "while x: x.pop()"
    100 loops, best of 3: 9.79 msec per loop

    Ama sonra çok fazla yükünüz var. Bunu ayrıca hesaplayın.

    >>> python -m timeit "x = [0]*100000"                   
    1000 loops, best of 3: 261 usec per loop

    Ek yükün çıkarılmasının burada yalnızca ek yükün zamanın küçük bir kısmı olduğu için makul olduğunu unutmayın .

    Örneğin, hem Ekleme Sıralaması hem de Zaman Sıralaması'nın zaten sıralı listeler için tamamen sıra dışı zamanlama davranışlarına sahip olduğunu belirtmek gerekir . Bu random.shuffle, zamanlamalarınızı batırmaktan kaçınmak istiyorsanız, bir tür arasında ihtiyacınız olacağı anlamına gelir .


1
usec ne anlama geliyor? mikrosaniye mi?
Hasan Iqbal

2
@HasanIqbalAnik Evet.
Veedrac

@StefanPochmann Çünkü birden çok kez örneklemeye çalışmaz.
Veedrac


@Veedrac Saf zamanlama yükünü çıkarma ifadesini göz önünde bulundurarak, hiç bir argüman verilmediğinde timeitbir passaçıklama yürütür , ki bu elbette biraz zaman alır. Herhangi bir argüman belirtilmemişse, passolacak değil bazı çıkarılarak böylece, idam 0.014her zamanlama gelen usecs yanlış olur.
Arne

99

İki kod / işlev bloğunu hızlı bir şekilde karşılaştırmak isterseniz şunları yapabilirsiniz:

import timeit

start_time = timeit.default_timer()
func1()
print(timeit.default_timer() - start_time)

start_time = timeit.default_timer()
func2()
print(timeit.default_timer() - start_time)

43

Timeit kullanmanın en kolay yolunu komut satırından buldum:

Verilen test.py :

def InsertionSort(): ...
def TimSort(): ...

timeit'i şu şekilde çalıştırın:

% python -mtimeit -s'import test' 'test.InsertionSort()'
% python -mtimeit -s'import test' 'test.TimSort()'

18

benim için bu en hızlı yol:

import timeit
def foo():
    print("here is my code to time...")


timeit.timeit(stmt=foo, number=1234567)

12
# Генерация целых чисел

def gen_prime(x):
    multiples = []
    results = []
    for i in range(2, x+1):
        if i not in multiples:
            results.append(i)
            for j in range(i*i, x+1, i):
                multiples.append(j)

    return results


import timeit

# Засекаем время

start_time = timeit.default_timer()
gen_prime(3000)
print(timeit.default_timer() - start_time)

# start_time = timeit.default_timer()
# gen_prime(1001)
# print(timeit.default_timer() - start_time)

7

Bu harika çalışıyor:

  python -m timeit -c "$(cat file_name.py)"

Windows eşdeğeri ne olurdu?
Shailen

2
Komut dosyası gerektiriyorsa parametreleri nasıl iletirsiniz?
Juuso Ohtonen

3

aşağıdakilerin her birinde aynı sözlüğü ayarlayalım ve yürütme süresini test edelim.

Kurulum argümanı temel olarak sözlüğü kurmaktır

Sayı, kodu 1000000 kez çalıştırmak içindir. Kurulum değil, stmt

Bunu çalıştırdığınızda, dizinin daha hızlı olduğunu görebilirsiniz. Görmek için birden fazla kez çalıştırabilirsiniz.

Kod temel olarak sözlükte c değerini almaya çalışır.

import timeit

print('Getting value of C by index:', timeit.timeit(stmt="mydict['c']", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000))
print('Getting value of C by get:', timeit.timeit(stmt="mydict.get('c')", setup="mydict={'a':5, 'b':6, 'c':7}", number=1000000))

İşte sonuçlarım, seninki farklı olacak.

dizine göre: 0.20900007452246427

tarafından elde: 0.54841166886888


Hangi python sürümünü kullanıyorsunuz?
Eduardo

3

kodunuzun tamamını timeit argümanı olarak iletmeniz yeterlidir:

import timeit

print(timeit.timeit(

"""   
limit = 10000
prime_list = [i for i in range(2, limit+1)]

for prime in prime_list:
    for elem in range(prime*2, max(prime_list)+1, prime):
        if elem in prime_list:
            prime_list.remove(elem)
"""   
, number=10))


0

Yerleşik timeit modülü en iyi IPython komut satırından çalışır.

Bir modül içindeki işlevleri zamanlamak için:

from timeit import default_timer as timer
import sys

def timefunc(func, *args, **kwargs):
    """Time a function. 

    args:
        iterations=3

    Usage example:
        timeit(myfunc, 1, b=2)
    """
    try:
        iterations = kwargs.pop('iterations')
    except KeyError:
        iterations = 3
    elapsed = sys.maxsize
    for _ in range(iterations):
        start = timer()
        result = func(*args, **kwargs)
        elapsed = min(timer() - start, elapsed)
    print(('Best of {} {}(): {:.9f}'.format(iterations, func.__name__, elapsed)))
    return result

0

Parametreleri kabul eden fonksiyonla Python REPL yorumlayıcısının nasıl kullanılacağına örnek.

>>> import timeit                                                                                         

>>> def naive_func(x):                                                                                    
...     a = 0                                                                                             
...     for i in range(a):                                                                                
...         a += i                                                                                        
...     return a                                                                                          

>>> def wrapper(func, *args, **kwargs):                                                                   
...     def wrapper():                                                                                    
...         return func(*args, **kwargs)                                                                  
...     return wrapper                                                                                    

>>> wrapped = wrapper(naive_func, 1_000)                                                                  

>>> timeit.timeit(wrapped, number=1_000_000)                                                              
0.4458435332577161                                                                                        

0

İki işlev oluşturup buna benzer bir şey çalıştırırsınız. Dikkat edin, elmayı elma ile karşılaştırmak için aynı sayıda yürütme / çalıştırma seçin.
Bu Python 3.7 altında test edilmiştir.

resim açıklamasını buraya girin Kopyalama kolaylığı için kod

!/usr/local/bin/python3
import timeit

def fibonacci(n):
    """
    Returns the n-th Fibonacci number.
    """
    if(n == 0):
        result = 0
    elif(n == 1):
        result = 1
    else:
        result = fibonacci(n-1) + fibonacci(n-2)
    return result

if __name__ == '__main__':
    import timeit
    t1 = timeit.Timer("fibonacci(13)", "from __main__ import fibonacci")
    print("fibonacci ran:",t1.timeit(number=1000), "milliseconds")
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.