上一篇文章我們介紹了基准測試,通過基准測試可以發現程序變慢了,那么是因為什么原因導致性能變慢的,需要進一步做代碼性能分析。python同樣提供了性能分析工具。
cProfile
cProfile是python默認的性能分析器,他只測量CPU時間,並不關心內存消耗和其他與內存相關聯的信息。
from time import sleep
import random
def random_list(start, end, length):
"""
生成隨機列表
:param start: 隨機開始數
:param end: 隨機結束數
:param length: 列表長度
"""
data_list = []
for i in range(length):
data_list.append(random.randint(start, end))
return data_list
def bubble_sort(arr):
"""
冒泡排序: 對列表進行排序
:param arr 列表
"""
n = len(arr)
sleep(1)
for i in range(n):
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr
if __name__ == '__main__':
get_data_list = random_list(1, 99, 10)
import cProfile
cProfile.run('bubble_sort({})'.format(get_data_list))
繼續使用上一篇文章中的例子,引用cProfile
模塊,run()
方法參數說明。
run(statement, filename=None, sort=-1)
- statement: 需要測試的代碼或者函數(函數名)
- fielname: 結果保存的位置, 默認為stdout
- sort: 結果排序方法,常用的有
cumtime
: 累積時間,name
: 函數名,line
: 行號
為了使結果統計出耗時部分,我們加了sleep
,結果如下:
❯ python demo.py
6 function calls in 1.004 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 1.004 1.004 <string>:1(<module>)
1 0.000 0.000 1.004 1.004 demo.py:19(bubble_sort)
1 0.000 0.000 1.004 1.004 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 {built-in method builtins.len}
1 1.004 1.004 1.004 1.004 {built-in method time.sleep}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
-
6 function calls in 1.004 seconds
6個函數調用被監控,耗時1.004秒。 -
ncalls
函數被調用的次數。如果這一列有兩個值,就表示有遞歸調用,第二個值是原生調用次數,第一個值是總調用次數。 -
tottime
函數內部消耗的總時間。(可以幫助優化) -
percall
是tottime除以ncalls,一個函數每次調用平均消耗時間。 -
cumtime
之前所有子函數消費時間的累計和。 -
filename:lineno(function)
被分析函數所在文件名、行號、函數名。
line_profiler
line_profiler
可以提供有關時間是如何在各行之間分配的信息,直白一點就是給出程序每行的耗時,在無法確定哪行語句最浪費時間,這很有用。
line_profiler是一個第三方模塊,需要安裝。
https://github.com/pyutils/line_profiler
from time import sleep
import random
def random_list(start, end, length):
"""
生成隨機列表
:param start: 隨機開始數
:param end: 隨機結束數
:param length: 列表長度
"""
data_list = []
for i in range(length):
data_list.append(random.randint(start, end))
return data_list
@profile
def bubble_sort(arr):
"""
冒泡排序: 對列表進行排序
:param arr 列表
"""
n = len(arr)
sleep(1)
for i in range(n):
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
return arr
if __name__ == '__main__':
get_data_list = random_list(1, 99, 10)
bubble_sort(get_data_list)
給需要監控的函數加上@profile
裝飾器。通過kernprof
命令運行文件(安裝完line_profiler生成的命令)。
參數說明:
-
-l
:以使用函數line_profiler -
-v
:以立即將結果打印到屏幕
運行結果:
kernprof -l -v demo.py
Wrote profile results to demo.py.lprof
Timer unit: 1e-06 s
Total time: 1.00416 s
File: demo.py
Function: bubble_sort at line 18
Line # Hits Time Per Hit % Time Line Contents
==============================================================
18 @profile
19 def bubble_sort(arr):
20 """
21 冒泡排序: 對列表進行排序
22 :param arr 列表
23 """
24 1 8.0 8.0 0.0 n = len(arr)
25 1 1004030.0 1004030.0 100.0 sleep(1)
26 11 15.0 1.4 0.0 for i in range(n):
27 55 44.0 0.8 0.0 for j in range(0, n - i - 1):
28 45 41.0 0.9 0.0 if arr[j] > arr[j + 1]:
29 20 21.0 1.1 0.0 arr[j], arr[j + 1] = arr[j + 1], arr[j]
30 1 1.0 1.0 0.0 return arr
輸出非常直觀,分成了6列。
Line #
:運行的代碼行號。Hits
:代碼行運行的次數。Time
:代碼行的執行時間,單位為微秒。Per Hit
:Time/Hits。% Time
:代碼行總執行時間所占的百分比。Line Contents
:代碼行的內容。
只需查看% Time
列,就可清楚地知道時間都花在了什么地方。
總結
性能測試分析站在項目層面是一個很龐大的話題,以前為測試工程師,關注的是性能工具的使用,以及用戶維度的性能[1];作為開發工程師,每個功能都是由一個個函數/方法
組成,我們去分析每個函數/方法
,甚至是每行代碼
的耗時,才能更好的進行代碼層面的性能優化。