Python 的 lru_cache 裝飾器是一個為自定義函數(shù)提供緩存功能的裝飾器。其內(nèi)部會在下次以相同參數(shù)調(diào)用該自定義函數(shù)時直接返回計算好的結(jié)果。通過緩存計算結(jié)果可以很好地提升性能结序。
1 從示例說起
假設(shè)我們有一個計算斐波那契數(shù)列的求和函數(shù),其內(nèi)部采用遞歸方式實現(xiàn)。
from xxx.clock_decorator import clock
@clock
def fibonacci(n):
if n<2:
return n
return fibonacci(n-2)+fibonacci(n-1)
if __name__=='__main__':
logging.info('fibonacci(6) -> %s',fibonacci(6))
運行結(jié)果:
其中的 clock_decorator 實現(xiàn)是一個可以輸出某個函數(shù)運行時長的裝飾器1。
從輸出結(jié)果中可以看出,存在著嚴重的重復計算情況呛每,比如 fibonacci(1) 就被計算了 5 次之多踩窖。這還只是計算 6 次的 fibonacci 函數(shù)。
2 優(yōu)化
上面的示例代碼加入 lru_cache 裝飾器:
運行結(jié)果:
這次不存在重復計算現(xiàn)象晨横,因此性能得到極大的提升洋腮。
3 比較
利用 cProfile 進行性能比較分析。它是一種確定性分析器手形,只測量 CPU 時間啥供,并不包含內(nèi)存消耗和其他與內(nèi)存相關(guān)聯(lián)的信息2。
假設(shè)我們需要計算 fibonacci(33) 求和值库糠。
(1)不使用 lru_cache 裝飾器
這個遞歸函數(shù)內(nèi)部總共調(diào)用了 1000 多萬次的 fibonacci() 函數(shù)伙狐!
(2)使用了 lru_cache 裝飾器
使用了 lru_cache 裝飾器之后,這個遞歸函數(shù)只需調(diào)用 100 多次fibonacci() 函數(shù)瞬欧!性能有了質(zhì)的提升贷屎。
4 lru_cache 裝飾器
lru_cache 裝飾器支持兩個入?yún)ⅲ耐暾x格式為3:
@functools.lru_cache(maxsize=128, typed=False)
參數(shù) | 默認值 | 說明 |
---|---|---|
maxsize | 128 | 表示緩存大小艘虎。如果設(shè)置為 None唉侄,則不限大小野建;如果超過緩存大小属划,則使用 LRU 策略清理緩存。緩存的大小限制可確保緩存不會無限制增長候生。LRU(Least Recently Used)同眯,即刪除最近最少使用的緩存數(shù)據(jù)。 |
typed | False | 如果為true唯鸭,不同類型的參數(shù)將會被分別緩存嗽测,比如區(qū)分浮點數(shù)與整型。 |
注意:由于使用了字典來存儲緩存肿孵,所以所裝飾的函數(shù)參數(shù)必須是可哈希的唠粥。
利用 cache_info() 函數(shù),我們還可以看到命中次數(shù) hits停做,未命中次數(shù) misses 晤愧,最大緩存數(shù)量 maxsize 和 當前緩存大小 currsize。使用方式是直接調(diào)用被裝飾函數(shù)的 cache_info()蛉腌,形如:fibonacci.cache_info())
官份。
只要某個函數(shù)遞歸調(diào)用并存在重復計算的情況只厘,這時就要記著使用 lru_cache 這個性能加速器。
- 說說在 Python 中如何實現(xiàn)輸出指定函數(shù)運行時長的裝飾器.
- 說說如何使用 Python 的 cProfile 模塊分析代碼性能.
- Python docs lru_cache.
- Luciano Ramalho (作者)舅巷,安道羔味,吳珂 (譯者).流暢的Python[M].人民郵電出版社,2017:323-326.