timeit是Python標(biāo)準(zhǔn)庫內(nèi)置的小工具,可以快速測試小段代碼的性能。
認(rèn)識timeit
創(chuàng)新互聯(lián)建站長期為上千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為小店企業(yè)提供專業(yè)的成都做網(wǎng)站、成都網(wǎng)站建設(shè)、成都外貿(mào)網(wǎng)站建設(shè),小店網(wǎng)站改版等技術(shù)服務(wù)。擁有10多年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。
timeit 函數(shù):
timeit.timeit(stmt, setup,timer, number)
參數(shù)說明:
stmt: statement的縮寫,你要測試的代碼或者語句,純文本,默認(rèn)值是 "pass"
setup: 在運(yùn)行stmt前的配置語句,純文本,默認(rèn)值也是 "pass"
timer: 計(jì)時(shí)器,一般忽略這個(gè)參數(shù)
number: stmt執(zhí)行的次數(shù),默認(rèn)是1000000,一百萬
repeat 函數(shù):
timeit.repeat(stmt, setup, timer, repeat, number)
是timeit的repeat版,可以指定重復(fù)timeit的次數(shù),默認(rèn)是3次,然后返回一個(gè)數(shù)組。
舉一個(gè)簡單的例子來說明用法:
import timeit
print(timeit.timeit('output = 10*5')) # 0.014560436829924583
print(timeit.repeat('output = 10*5')) # [0.01492984383367002, 0.01342877489514649, 0.013638464966788888]
嗯,看上去沒毛病,實(shí)際上誰也不會(huì)去測沒有意義的加減乘除,我們需要測試自己的代碼。
測試多行代碼
測試多行代碼可以用分號來連接語句。
print(timeit.timeit(stmt='a=10;b=10;sum=a+b'))
也可以用三引號來寫stmt。
import timeit
import_module = "import random"
testcode = '''
def test():
return random.randint(10, 100)
'''
print(timeit.repeat(stmt=testcode, setup=import_module))
但是其實(shí)都挺扯的,自己的代碼會(huì)那么簡單?我們是模塊化編程。
測試模塊中的函數(shù)
如果你要測試的函數(shù)都在一個(gè)模塊里,可以這樣寫timeit。
import timeit
import random
import arrow
def stupid1():
return random.randint(1, 10)
def stupid2():
return stupid1()
def stupid3():
return arrow.now()
print(timeit.timeit('stupid1()', setup='from mainimport stupid1'))
print(timeit.timeit('stupid2()', setup='from mainimport stupid2'))
print(timeit.timeit('stupid3()', setup='from mainimport stupid3', number=100))
寫成上面這樣的其實(shí)還是單行的模式。
借用default_timer
timeit自帶的default_timer可以借來用一下。
import timeit
import random
def test():
return random.randint(10, 100)
starttime = timeit.default_timer()
print("The start time is :",starttime)
test()
print("The time difference is :", timeit.default_timer() - starttime)
命令行的用法
timeit還支持命令行的調(diào)用方式,不過我覺得太累了,沒必要去嘗試。
C:\pythontest>python -m timeit -s 'text="hello world"'
20000000 loops, best of 5: 13.1 nsec per loop
分享一個(gè)案例
2月29那天,我想今年是閏年啊,計(jì)算閏年有幾種算法啊?孔乙己說有3種:
def is_leap_year_0(year):
if year % 4 == 0:
if year % 100 == 0:
if year % 400 == 0:
return True
else:
return False
else:
return True
else:
return False
def is_leap_year_1(year):
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
def is_leap_year_2(year):
if year % 400 == 0:
return True
if year % 100 == 0:
return False
if year % 4 == 0:
return True
return False
這三種方法那種最好啊?這個(gè)不能一概而論吧,因?yàn)橐茨愕膮?shù)是什么,比如1991年不是閏年,方法0和方法1直接就返回了,但方法2還需要走到最后一個(gè)if才知道不是閏年。再比如2020年,方法2直接就返回了,但是方法0和1需要走到最里層的if才得到結(jié)果。所以我們需要取樣測試才公平,比如從1900年到2900年,每個(gè)函數(shù)都跑10000遍。
timeit就不太方便了,它接受的參數(shù)哪能那么復(fù)雜,我們需要包裝一下。
def perf_test(method):
years = range(1900, 2900)
if method == 0:
for y in years:
is_leap_year_0(y)
if method == 1:
for y in years:
is_leap_year_1(y)
if method == 2:
for y in years:
is_leap_year_2(y)
print(timeit('perf_test(0)', setup='from mainimport perf_test', number=10000))
print(timeit('perf_test(1)', setup='from mainimport perf_test', number=10000))
print(timeit('perf_test(2)', setup='from mainimport perf_test', number=10000))
你們猜猜看哪個(gè)方法結(jié)果最好?你一定想不到。
1.6432780250906944
1.7527272370643914
0.0023122059646993876
其他的思路
timeit其實(shí)還是太弱了,隨便測測還湊合,如果真要檢查性能問題還是需要用更專業(yè)的手段。比如:
PyCharm Profiler (Pro版功能)
cProfile
pycallgraph
memory_profiler