讓你的Python提速30%!(下)
- 2020 年 2 月 23 日
- 筆記
編輯 | sunlei 發布 | ATYUN訂閱號
使它更快
現在進入有趣的部分。讓我們幫您的Python程式運行得更快。我(基本上)不會向您展示一些能夠神奇地解決性能問題的黑客、技巧和程式碼片段。這更多的是關於一般的想法和策略,當使用時,它們可以對性能產生巨大的影響,在某些情況下可以提高30%的速度。
使用內置數據類型
這一點很明顯。內置數據類型非常快,特別是與我們的自定義類型(如樹或鏈接列表)相比。這主要是因為內置程式碼是用C實現的,在用Python編寫程式碼時,我們在速度上無法真正匹配。
快取/記憶lru_cache
用一個簡單的例子來重複一下:
import functools import time # caching up to 12 different results @functools.lru_cache(maxsize=12) def slow_func(x): time.sleep(2) # Simulate long computation return x slow_func(1) # ... waiting for 2 sec before getting result slow_func(1) # already cached - result returned instantaneously! slow_func(3) # ... waiting for 2 sec before getting result
上面的函數使用time.sleep模擬繁重的計算。當第一次用參數1調用時,它等待2秒,然後才返回結果。再次調用時,結果已被快取,因此它跳過函數體並立即返回結果。
使用局部變數
這與在每個作用域中查找變數的速度有關。我編寫每個作用域,因為它不僅僅是使用局部變數和全局變數。實際上,查找速度甚至在函數中的局部變數(最快)、類級屬性(例如self.name-slower)和全局(例如time.time(最慢))之間也存在差異。
你可以通過使用看起來不必要的(直接的無用的)任務來提高性能,比如:
# Example #1 class FastClass: def do_stuff(self): temp = self.value # this speeds up lookup in loop for i in range(10000): ... # Do something with `temp` here # Example #2 import random def fast_function(): r = random.random for i in range(10000): print(r()) # calling `r()` here, is faster than global random.random()
使用函數
這似乎有悖常理,因為調用函數會將更多的東西放到堆棧中,並從函數返回中產生開銷,但這與前面的一點有關。如果只將整個程式碼放在一個文件中而不將其放在函數中,則會因為全局變數而慢得多。因此,只需將整個程式碼包裝在main函數中並調用一次,就可以加快程式碼的速度,如下所示:
def main(): ... # All your previously global code main()
不訪問屬性
另一個可能會減慢程式速度的是點運算符(.),它在訪問對象屬性時使用。此運算符使用_getattribute__觸發字典查找,這會在程式碼中產生額外的開銷。那麼,我們如何才能真正避免(限制)使用它呢?
# Slow: import re def slow_func(): for i in range(10000): re.findall(regex, line) # Slow! # Fast: from re import findall def fast_func(): for i in range(10000): findall(regex, line) # Faster!
謹防字元串
在循環中使用例如module(%s)或.format()運行時,對字元串的操作可能會非常慢。我們還有什麼更好的選擇?根據Raymond Hettinger最近的推文,我們唯一應該使用的是f-string,它是最可讀、最簡潔、最快的方法。因此,根據這條推文,這是你可以使用的方法列表-從最快到最慢:
f'{s} {t}' # Fast! s + ' ' + t ' '.join((s, t)) '%s %s' % (s, t) '{} {}'.format(s, t) Template('$s $t').substitute(s=s, t=t) # Slow!
生成器本身並不是更快的,因為它們允許延遲計算,這節省了記憶體而不是時間。但是,節省的記憶體可能會導致程式實際上運行得更快。怎樣?好吧,如果您有一個大型數據集,並且不使用生成器(迭代器),那麼數據可能會溢出cpu L1快取,這將顯著減慢在記憶體中查找值的速度。
說到性能,很重要的一點是CPU可以保存它正在處理的所有數據,儘可能接近地保存在快取中。你可以看Raymond Hettingers的演講,他提到了這些問題。
結論
優化的第一條規則是不要這樣做。但是,如果你真的需要的話,我希望這幾條建議能幫到你。但是,在優化程式碼時要小心,因為它可能會導致程式碼難以閱讀,因此難以維護,這可能會超過優化的好處。
原文鏈接:
https://towardsdatascience.com/making-python-programs-blazingly-fast-c1cd79bd1b32