Benchmarking dan Profiling
Dalam bab ini, kita akan mempelajari bagaimana pembandingan dan pembuatan profil membantu dalam menangani masalah kinerja.
Misalkan kita telah menulis kode dan itu memberikan hasil yang diinginkan juga tetapi bagaimana jika kita ingin menjalankan kode ini sedikit lebih cepat karena kebutuhan telah berubah. Dalam hal ini, kita perlu mencari tahu bagian mana dari kode kita yang memperlambat keseluruhan program. Dalam hal ini, pembandingan dan pembuatan profil dapat berguna.
Apa itu Benchmarking?
Benchmarking bertujuan untuk mengevaluasi sesuatu dengan membandingkannya dengan standar. Namun, pertanyaan yang muncul di sini adalah apa yang akan menjadi benchmarking dan mengapa kita membutuhkannya dalam hal pemrograman perangkat lunak. Pembandingan kode berarti seberapa cepat kode dijalankan dan di mana kemacetannya. Salah satu alasan utama untuk pembandingan adalah karena ia mengoptimalkan kode.
Bagaimana cara kerja pembandingan?
Jika kita berbicara tentang cara kerja benchmarking, kita perlu memulai dengan benchmarking seluruh program sebagai satu keadaan saat ini, kemudian kita dapat menggabungkan tolok ukur mikro dan kemudian menguraikan program menjadi program yang lebih kecil. Untuk menemukan hambatan dalam program kami dan mengoptimalkannya. Dengan kata lain, kita dapat memahaminya sebagai pemecahan masalah yang besar dan berat menjadi rangkaian masalah yang lebih kecil dan sedikit lebih mudah untuk dioptimalkan.
Modul Python untuk benchmarking
Di Python, kami memiliki modul default untuk benchmarking yang disebut timeit. Dengan bantuantimeit modul, kita dapat mengukur kinerja sedikit kode Python dalam program utama kita.
Contoh
Dalam skrip Python berikut, kami mengimpor file timeit modul, yang selanjutnya mengukur waktu yang dibutuhkan untuk menjalankan dua fungsi - functionA dan functionB -
import timeit
import time
def functionA():
print("Function A starts the execution:")
print("Function A completes the execution:")
def functionB():
print("Function B starts the execution")
print("Function B completes the execution")
start_time = timeit.default_timer()
functionA()
print(timeit.default_timer() - start_time)
start_time = timeit.default_timer()
functionB()
print(timeit.default_timer() - start_time)
Setelah menjalankan script diatas, kita akan mendapatkan waktu eksekusi dari kedua fungsi tersebut seperti gambar dibawah.
Keluaran
Function A starts the execution:
Function A completes the execution:
0.0014599495514175942
Function B starts the execution
Function B completes the execution
0.0017024724827479076
Menulis timer kita sendiri menggunakan fungsi dekorator
Dengan Python, kita bisa membuat timer sendiri, yang akan berfungsi seperti timeitmodul. Ini dapat dilakukan dengan bantuandecoratorfungsi. Berikut adalah contoh timer khusus -
import random
import time
def timer_func(func):
def function_timer(*args, **kwargs):
start = time.time()
value = func(*args, **kwargs)
end = time.time()
runtime = end - start
msg = "{func} took {time} seconds to complete its execution."
print(msg.format(func = func.__name__,time = runtime))
return value
return function_timer
@timer_func
def Myfunction():
for x in range(5):
sleep_time = random.choice(range(1,3))
time.sleep(sleep_time)
if __name__ == '__main__':
Myfunction()
Skrip python di atas membantu dalam mengimpor modul waktu acak. Kami telah membuat fungsi dekorator timer_func (). Ini memiliki fungsi function_timer () di dalamnya. Sekarang, fungsi bersarang akan mengambil waktu sebelum memanggil fungsi yang diteruskan. Kemudian menunggu fungsi kembali dan mengambil waktu berakhir. Dengan cara ini, akhirnya kita bisa membuat script python mencetak waktu eksekusi. Skrip akan menghasilkan keluaran seperti yang ditunjukkan di bawah ini.
Keluaran
Myfunction took 8.000457763671875 seconds to complete its execution.
Apa itu pembuatan profil?
Kadang-kadang programmer ingin mengukur beberapa atribut seperti penggunaan memori, kompleksitas waktu atau penggunaan instruksi tertentu tentang program untuk mengukur kapabilitas nyata dari program itu. Pengukuran semacam itu tentang program disebut profiling. Profiling menggunakan analisis program dinamis untuk melakukan pengukuran tersebut.
Di bagian selanjutnya, kita akan belajar tentang Modul Python untuk Profiling yang berbeda.
cProfile - modul inbuilt
cProfileadalah modul built-in Python untuk pembuatan profil. Modul ini adalah ekstensi-C dengan overhead yang masuk akal yang membuatnya cocok untuk membuat profil program yang berjalan lama. Setelah menjalankannya, ia mencatat semua fungsi dan waktu eksekusi. Ini sangat kuat tetapi terkadang agak sulit untuk ditafsirkan dan ditindaklanjuti. Pada contoh berikut, kami menggunakan cProfile pada kode di bawah ini -
Contoh
def increment_global():
global x
x += 1
def taskofThread(lock):
for _ in range(50000):
lock.acquire()
increment_global()
lock.release()
def main():
global x
x = 0
lock = threading.Lock()
t1 = threading.Thread(target=taskofThread, args=(lock,))
t2 = threading.Thread(target= taskofThread, args=(lock,))
t1.start()
t2.start()
t1.join()
t2.join()
if __name__ == "__main__":
for i in range(5):
main()
print("x = {1} after Iteration {0}".format(i,x))
Kode di atas disimpan di thread_increment.pymengajukan. Sekarang, jalankan kode dengan cProfile pada baris perintah sebagai berikut -
(base) D:\ProgramData>python -m cProfile thread_increment.py
x = 100000 after Iteration 0
x = 100000 after Iteration 1
x = 100000 after Iteration 2
x = 100000 after Iteration 3
x = 100000 after Iteration 4
3577 function calls (3522 primitive calls) in 1.688 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
5 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap>:103(release)
5 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap>:143(__init__)
5 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap>:147(__enter__)
… … … …
Dari keluaran di atas, jelas bahwa cProfile mencetak semua 3577 fungsi yang dipanggil, dengan waktu yang dihabiskan untuk masing-masing fungsi dan berapa kali fungsi tersebut dipanggil. Berikut adalah kolom yang kami dapatkan di output -
ncalls - Ini adalah jumlah panggilan yang dilakukan.
tottime - Ini adalah total waktu yang dihabiskan dalam fungsi yang diberikan.
percall - Ini mengacu pada hasil bagi waktu dibagi dengan ncalls.
cumtime- Ini adalah waktu kumulatif yang dihabiskan untuk ini dan semua subfungsi. Bahkan akurat untuk fungsi rekursif.
percall - Ini adalah hasil bagi dari cumtime dibagi dengan panggilan primitif.
filename:lineno(function) - Ini pada dasarnya menyediakan data masing-masing dari setiap fungsi.