問題
你想測試你的程序運(yùn)行所花費(fèi)的時(shí)間并做性能測試。
解決方案
如果你只是簡單的想測試下你的程序整體花費(fèi)的時(shí)間, 通常使用Unix時(shí)間函數(shù)就行了,比如:
1
2
3
4
5
|
bash % time python3 someprogram.py real 0m13 . 937s user 0m12 . 162s sys 0m0 . 098s bash % |
如果你還需要一個(gè)程序各個(gè)細(xì)節(jié)的詳細(xì)報(bào)告,可以使用 cProfile 模塊:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
bash % python3 - m cProfile someprogram.py 859647 function calls in 16.016 CPU seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 263169 0.080 0.000 0.080 0.000 someprogram.py: 16 (frange) 513 0.001 0.000 0.002 0.000 someprogram.py: 30 (generate_mandel) 262656 0.194 0.000 15.295 0.000 someprogram.py: 32 (<genexpr>) 1 0.036 0.036 16.077 16.077 someprogram.py: 4 (<module>) 262144 15.021 0.000 15.021 0.000 someprogram.py: 4 (in_mandelbrot) 1 0.000 0.000 0.000 0.000 os.py: 746 (urandom) 1 0.000 0.000 0.000 0.000 png.py: 1056 (_readable) 1 0.000 0.000 0.000 0.000 png.py: 1073 (Reader) 1 0.227 0.227 0.438 0.438 png.py: 163 (<module>) 512 0.010 0.000 0.010 0.000 png.py: 200 (group) ... bash % |
不過通常情況是介于這兩個(gè)極端之間。比如你已經(jīng)知道代碼運(yùn)行時(shí)在少數(shù)幾個(gè)函數(shù)中花費(fèi)了絕大部分時(shí)間。 對于這些函數(shù)的性能測試,可以使用一個(gè)簡單的裝飾器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# timethis.py import time from functools import wraps def timethis(func): @wraps (func) def wrapper( * args, * * kwargs): start = time.perf_counter() r = func( * args, * * kwargs) end = time.perf_counter() print ( '{}.{} : {}' . format (func.__module__, func.__name__, end - start)) return r return wrapper |
要使用這個(gè)裝飾器,只需要將其放置在你要進(jìn)行性能測試的函數(shù)定義前即可,比如:
1
2
3
4
5
6
7
8
|
>>> @timethis ... def countdown(n): ... while n > 0 : ... n - = 1 ... >>> countdown( 10000000 ) __main__.countdown : 0.803001880645752 >>> |
要測試某個(gè)代碼塊運(yùn)行時(shí)間,你可以定義一個(gè)上下文管理器,例如:
1
2
3
4
5
6
7
8
9
10
|
from contextlib import contextmanager @contextmanager def timeblock(label): start = time.perf_counter() try : yield finally : end = time.perf_counter() print ( '{} : {}' . format (label, end - start)) |
下面是使用這個(gè)上下文管理器的例子:
1
2
3
4
5
6
7
|
>>> with timeblock( 'counting' ): ... n = 10000000 ... while n > 0 : ... n - = 1 ... counting : 1.5551159381866455 >>> |
對于測試很小的代碼片段運(yùn)行性能,使用 timeit 模塊會很方便,例如:
1
2
3
4
5
6
|
>>> from timeit import timeit >>> timeit( 'math.sqrt(2)' , 'import math' ) 0.1432319980012835 >>> timeit( 'sqrt(2)' , 'from math import sqrt' ) 0.10836604500218527 >>> |
timeit 會執(zhí)行第一個(gè)參數(shù)中語句100萬次并計(jì)算運(yùn)行時(shí)間。 第二個(gè)參數(shù)是運(yùn)行測試之前配置環(huán)境。如果你想改變循環(huán)執(zhí)行次數(shù), 可以像下面這樣設(shè)置 number 參數(shù)的值:
1
2
3
4
5
|
>>> timeit( 'math.sqrt(2)' , 'import math' , number = 10000000 ) 1.434852126003534 >>> timeit( 'sqrt(2)' , 'from math import sqrt' , number = 10000000 ) 1.0270336690009572 >>> |
討論
當(dāng)執(zhí)行性能測試的時(shí)候,需要注意的是你獲取的結(jié)果都是近似值。 time.perf_counter() 函數(shù)會在給定平臺上獲取最高精度的計(jì)時(shí)值。 不過,它仍然還是基于時(shí)鐘時(shí)間,很多因素會影響到它的精確度,比如機(jī)器負(fù)載。 如果你對于執(zhí)行時(shí)間更感興趣,使用 time.process_time() 來代替它。例如:
1
2
3
4
5
6
7
8
9
10
|
from functools import wraps def timethis(func): @wraps (func) def wrapper( * args, * * kwargs): start = time.process_time() r = func( * args, * * kwargs) end = time.process_time() print ( '{}.{} : {}' . format (func.__module__, func.__name__, end - start)) return r return wrapper |
最后,如果你想進(jìn)行更深入的性能分析,那么你需要詳細(xì)閱讀 time 、timeit 和其他相關(guān)模塊的文檔。 這樣你可以理解和平臺相關(guān)的差異以及一些其他陷阱。 還可以參考13.13小節(jié)中相關(guān)的一個(gè)創(chuàng)建計(jì)時(shí)器類的例子。
以上就是Python如何給你的程序做性能測試的詳細(xì)內(nèi)容,更多關(guān)于Python做性能測試的資料請關(guān)注服務(wù)器之家其它相關(guān)文章!
原文鏈接:https://python3-cookbook.readthedocs.io/zh_CN/latest/c14/p13_profiling_and_timing_your_program.html