在本章中,我們將學習基準測試和分析如何幫助解決性能問題。
假設我們編寫了一個代碼并且它也提供了所需的結果,但是如果我們想要更快地運行此代碼,因為需求已經改變了。在這種情況下,我們需要找出代碼的哪些部分正在減慢整個程序的速度。在這種情況下,基準測試和分析可能很有用。
什么是基準測試?
基準測試旨在通過??與標準進行比較來評估某些內容。然而,這里出現的問題是什么是基準測試以及為什么在軟件編程的情況下我們需要它。對代碼進行基準測試意味著代碼的執行速度和瓶頸所在。基準測試的一個主要原因是它優化了代碼。
基準測試如何運作?
如果我們談論基準測試的工作,我們需要首先將整個程序作為一個當前狀態進行基準測試然后我們可以結合微基準測試然后將程序分解為更小的程序。為了找到我們計劃中的瓶頸并進行優化。換句話說,我們可以將它理解為將大而難的問題分解為一系列更小且更容易優化它們的問題。
用于基準測試的python模塊
在python中,我們有一個默認的基準測試模塊,稱為 timeit 。在 timeit 模塊的幫助下,我們可以在主程序中測量一小部分python代碼的性能。
例
在下面的python腳本中,我們將導入 timeit 模塊,該模塊進一步測量執行兩個函數所需的時間 - functiona 和 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)
運行上面的腳本后,我們將得到兩個函數的執行時間,如下所示。
輸出
function a starts the execution: function a completes the execution: 0.0014599495514175942 function b starts the execution function b completes the execution 0.0017024724827479076
使用裝飾器功能編寫我們自己的計時器
在python中,我們可以創建自己的計時器,它就像 timeit 模塊一樣。它可以在 裝飾器 功能的幫助下完成。以下是自定義計時器的示例
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()
上面的python腳本有助于導入隨機時間模塊。我們創建了timer_func()裝飾器函數。這里面有function_timer()函數?,F在,嵌套函數將在調用傳入函數之前獲取時間。然后它等待函數返回并獲取結束時間。這樣,我們終于可以讓python腳本打印執行時間了。該腳本將生成輸出,如下所示。
輸出
myfunction took 8.000457763671875 seconds to complete its execution.
什么是剖析?
有時程序員想要測量一些屬性,如使用內存,時間復雜度或使用有關程序的特定指令來測量程序的實際能力。這種關于程序的測量稱為剖析。分析使用動態程序分析來進行此類測量。
在隨后的部分中,我們將了解用于分析的不同python模塊。
cprofile - 內置模塊
cprofile 是一個用于分析的python內置模塊。該模塊是一個c擴展,具有合理的開銷,使其適用于分析長時間運行的程序。運行后,它會記錄所有功能和執行時間。它非常強大,但有時難以解釋和采取行動。在以下示例中,我們在下面的代碼中使用cprofile
例
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))
上面的代碼保存在 thread_increment.py 文件中。現在,在命令行上使用cprofile執行代碼,如下所示 -
(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 :103(release) 5 0.000 0.000 0.000 0.000 :143(__init__) 5 0.000 0.000 0.000 0.000 :147(__enter__) … … … …
從上面的輸出中可以清楚地看出,cprofile打印出所有被調用的3577個函數,其中包括每個函數所花費的時間以及它們被調用的次數。以下是我們輸出的列
- ncalls - 這是調用的次數。
- tottime - 這是在給定函數中花費的總時間。
- percall - 它指的是tottime除以ncalls的商。
- cumtime - 這是在這個和所有子功能中花費的累積時間。 它對于遞歸函數甚至是準確的。
- percall - 它是cumtime除以原始調用的商。
- filename:lineno(function) - 它基本上提供了每個函數的相應數據。