Jawaban:
Cara timeit bekerja adalah dengan menjalankan kode pengaturan sekali dan kemudian membuat panggilan berulang ke serangkaian pernyataan. Jadi, jika Anda ingin menguji penyortiran, diperlukan kehati-hatian agar satu pass pada in-place sort tidak mempengaruhi pass berikutnya dengan data yang sudah diurutkan (yang, tentu saja, akan membuat Timsort benar-benar bersinar karena berkinerja terbaik. ketika data sudah dipesan sebagian).
Berikut adalah contoh cara menyiapkan tes untuk penyortiran:
>>> 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
Perhatikan bahwa rangkaian pernyataan membuat salinan baru dari data yang tidak disortir pada setiap pass.
Juga, perhatikan teknik pengaturan waktu menjalankan rangkaian pengukuran tujuh kali dan hanya menjaga waktu terbaik - ini benar-benar dapat membantu mengurangi distorsi pengukuran karena proses lain yang berjalan pada sistem Anda.
Itulah tips saya untuk menggunakan timeit dengan benar. Semoga ini membantu :-)
.repeat(7,1000)
sudah melakukan ini (dengan menggunakan benih yang sama)! Jadi solusi Anda adalah IMO yang sempurna.
.repeat(7, 1000)
vs .repeat(2, 3500)
vs .repeat(35, 200
) harus bergantung pada bagaimana kesalahan karena beban sistem dibandingkan dengan kesalahan karena variabilitas input. Dalam kasus ekstrem jika sistem Anda selalu di bawah beban berat, dan Anda melihat ekor tipis panjang di sebelah kiri distribusi waktu eksekusi (ketika Anda menangkapnya dalam keadaan idle langka), Anda bahkan mungkin menemukan .repeat(7000,1)
lebih berguna daripada .repeat(7,1000)
jika Anda tidak dapat menganggarkan lebih dari 7000 berjalan.
Jika Anda ingin menggunakan timeit
sesi Python interaktif, ada dua opsi yang mudah:
Gunakan shell IPython . Ini fitur %timeit
fungsi khusus yang nyaman :
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
Dalam juru bahasa Python standar, Anda dapat mengakses fungsi dan nama lain yang Anda tentukan sebelumnya selama sesi interaktif dengan mengimpornya dari __main__
dalam pernyataan pengaturan:
>>> 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]
from __main__ import f
tekniknya. Saya tidak berpikir ini dikenal secara luas sebagaimana mestinya. Ini berguna dalam kasus-kasus seperti ini di mana panggilan fungsi atau metode sedang diatur waktunya. Dalam kasus lain (mengatur serangkaian langkah), ini kurang membantu karena memperkenalkan overhead panggilan fungsi.
%timeit f(x)
sys._getframe(N).f_globals
) seharusnya menjadi default dari awal.
Saya akan memberi Anda rahasia: cara terbaik untuk menggunakan timeit
adalah di baris perintah.
Di baris perintah, timeit
lakukan analisis statistik yang tepat: ini memberi tahu Anda berapa lama waktu yang paling singkat. Ini bagus karena semua kesalahan dalam pengaturan waktu adalah positif. Jadi waktu tersingkat memiliki kesalahan paling sedikit di dalamnya. Tidak ada cara untuk mendapatkan kesalahan negatif karena komputer tidak pernah dapat menghitung lebih cepat daripada yang bisa dihitung!
Jadi, antarmuka baris perintah:
%~> python -m timeit "1 + 2"
10000000 loops, best of 3: 0.0468 usec per loop
Itu cukup sederhana, eh?
Anda dapat mengatur barang-barang:
%~> python -m timeit -s "x = range(10000)" "sum(x)"
1000 loops, best of 3: 543 usec per loop
yang bermanfaat juga!
Jika Anda ingin banyak baris, Anda bisa menggunakan kelanjutan otomatis shell atau menggunakan argumen terpisah:
%~> python -m timeit -s "x = range(10000)" -s "y = range(100)" "sum(x)" "min(y)"
1000 loops, best of 3: 554 usec per loop
Itu memberikan pengaturan
x = range(1000)
y = range(100)
dan waktu
sum(x)
min(y)
Jika Anda ingin memiliki skrip yang lebih panjang, Anda mungkin tergoda untuk pindah ke timeit
dalam skrip Python. Saya sarankan menghindari itu karena analisis dan pengaturan waktu hanya lebih baik di baris perintah. Sebaliknya, saya cenderung membuat skrip shell:
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
Ini bisa memakan waktu sedikit lebih lama karena beberapa inisialisasi, tetapi biasanya itu bukan masalah besar.
Tetapi bagaimana jika Anda ingin menggunakan timeit
di dalam modul Anda?
Nah, cara sederhana adalah melakukan:
def function(...):
...
timeit.Timer(function).timeit(number=NUMBER)
dan itu memberi Anda waktu kumulatif ( bukan minimum!) untuk menjalankan jumlah itu.
Untuk mendapatkan analisis yang baik, gunakan .repeat
dan ambil minimum:
min(timeit.Timer(function).repeat(repeat=REPEATS, number=NUMBER))
Anda biasanya harus menggabungkan ini dengan functools.partial
bukannya lambda: ...
menurunkan overhead. Dengan demikian Anda dapat memiliki sesuatu seperti:
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
Anda juga dapat melakukan:
timeit.timeit("...", setup="from __main__ import ...", number=NUMBER)
yang akan memberi Anda sesuatu yang lebih dekat ke antarmuka dari command-line, tetapi dengan cara yang jauh lebih keren. The "from __main__ import ..."
memungkinkan Anda menggunakan kode dari modul utama Anda dalam lingkungan buatan yang dibuat oleh timeit
.
Perlu dicatat bahwa ini adalah pembungkus yang nyaman Timer(...).timeit(...)
dan karenanya tidak terlalu bagus dalam hal waktu. Saya pribadi jauh lebih suka menggunakan Timer(...).repeat(...)
seperti yang saya tunjukkan di atas.
Ada beberapa peringatan dengan timeit
yang menahan di mana-mana.
Overhead tidak diperhitungkan. Katakan Anda ingin waktu x += 1
, untuk mengetahui berapa lama penambahan:
>>> python -m timeit -s "x = 0" "x += 1"
10000000 loops, best of 3: 0.0476 usec per loop
Yah, ini bukan 0,0476 μs. Anda hanya tahu bahwa itu kurang dari itu. Semua kesalahan adalah positif.
Jadi cobalah dan temukan overhead murni :
>>> python -m timeit -s "x = 0" ""
100000000 loops, best of 3: 0.014 usec per loop
Itu overhead 30% baik hanya dari waktu! Ini secara besar-besaran dapat mengubah pengaturan waktu relatif. Tetapi Anda hanya benar-benar peduli tentang penambahan waktu; timing pencarian untuk x
juga perlu dimasukkan dalam overhead:
>>> python -m timeit -s "x = 0" "x"
100000000 loops, best of 3: 0.0166 usec per loop
Perbedaannya tidak jauh lebih besar, tetapi ada di sana.
Metode bermutasi berbahaya.
>>> python -m timeit -s "x = [0]*100000" "while x: x.pop()"
10000000 loops, best of 3: 0.0436 usec per loop
Tapi itu sama sekali salah! x
adalah daftar kosong setelah iterasi pertama. Anda harus menginisialisasi ulang:
>>> python -m timeit "x = [0]*100000" "while x: x.pop()"
100 loops, best of 3: 9.79 msec per loop
Tetapi kemudian Anda memiliki banyak overhead. Akun untuk itu secara terpisah.
>>> python -m timeit "x = [0]*100000"
1000 loops, best of 3: 261 usec per loop
Perhatikan bahwa mengurangi overhead adalah wajar di sini hanya karena overhead adalah sebagian kecil dari waktu.
Sebagai contoh Anda, perlu dicatat bahwa kedua Sisipan Penyisipan dan Tim Mengurutkan memiliki perilaku waktu yang sama sekali tidak biasa untuk daftar yang sudah diurutkan. Ini berarti Anda akan memerlukan random.shuffle
antara macam-macam jika Anda ingin menghindari merusak waktu Anda.
timeit
dari suatu program tetapi berfungsi dengan cara yang sama seperti baris perintah? .
timeit
jalankan pass
pernyataan ketika tidak ada argumen yang diberikan, yang tentu saja membutuhkan waktu. Jika ada argumen yang diberikan, tidakpass
akan dieksekusi, jadi kurangi beberapa usecs dari setiap waktu akan salah. 0.014
bagi saya, ini adalah cara tercepat:
import timeit
def foo():
print("here is my code to time...")
timeit.timeit(stmt=foo, number=1234567)
# Генерация целых чисел
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)
Ini sangat bagus:
python -m timeit -c "$(cat file_name.py)"
memungkinkan pengaturan kamus yang sama di masing-masing berikut dan menguji waktu eksekusi.
Argumen pengaturan pada dasarnya adalah mengatur kamus
Nomor untuk menjalankan kode 1000000 kali. Bukan pengaturan tetapi stmt
Ketika Anda menjalankan ini, Anda dapat melihat bahwa indeks jauh lebih cepat daripada mendapatkan. Anda dapat menjalankannya beberapa kali untuk melihatnya.
Kode ini pada dasarnya mencoba untuk mendapatkan nilai c dalam kamus.
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))
Inilah hasil saya, hasil Anda akan berbeda.
berdasarkan indeks: 0.20900007452246427
oleh get: 0.54841166886888
cukup kirimkan seluruh kode Anda sebagai argumen timeit:
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))
import timeit
def oct(x):
return x*x
timeit.Timer("for x in range(100): oct(x)", "gc.enable()").timeit()
gc.enable()
?
Modul timeit bawaan berfungsi paling baik dari baris perintah IPython.
Fungsi waktu dari dalam modul:
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
Contoh cara menggunakan interpreter Python REPL dengan fungsi yang menerima parameter.
>>> 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
Anda akan membuat dua fungsi dan menjalankan sesuatu yang mirip dengan ini. Perhatikan, Anda ingin memilih jumlah eksekusi / lari yang sama untuk membandingkan apel dengan apel.
Ini diuji dengan Python 3.7.
Berikut adalah kode untuk kemudahan menyalinnya
!/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")
timsort(a)
dan ambil perbedaan :-)