用這 6 種方式優化程式碼,讓你的 Python 速度提升 30%!
用這 6 種方式優化程式碼,讓你的 Python 速度提升 30%!
News from: TechOrange
Python 已經得到了全球工程師的喜愛,但是還是遭到一些人的詬病,原因之一就是認為它運作緩慢。其實某個特定程式(無論使用何種程式語言)的運行速度是快還是慢,在很大程度上取決於編寫該程式的開發人員自身素質,以及他們編寫高效程式碼的能力。Medium 上一位網友就詳細講了講如何讓 python 提速 30%,以此證明程式碼跑得慢不是 python 的問題,而是程式碼本身的問題。
Web site: https://towardsdatascience.com/making-python-programs-blazingly-fast-c1cd79bd1b32
首先,找出拖慢程式的程式碼
在開始進行任何優化之前,我們首先需要找出程式碼的哪些部分使整個程式變慢。有時程式的問題很明顯,但是如果你一時不知道問題出在哪裡,那麼這裡有一些可能的選項:
注意:這是我將用於演示的程式,它將進行指數計算(取自 Python 文檔):
▌最簡單的方法:Unix 時間命令
首先,最簡單最偷懶的方法:Unix 時間命令。
如果你只能直到整個程式的運行時間,這樣就夠了,但通常這還遠遠不夠。
▌更詳細的分析:cProfile
另外一個指令是 cProfile,但是它提供的資訊過於詳細了。(完整程式碼 在這裡)
在這裡,我們使用 cProfile 模組和 time 參數運行測試腳本,以便按內部時間(cumtime)對行進行排序。這給了我們很多資訊,你在上面看到的行大約是實際輸出的 10%。由此可見,exp 函數是罪魁禍首,現在我們可以更詳細地瞭解時序和性能分析。
▌時序特定功能
現在我們知道了應當主要關注哪裡,我們可能想對運行速度緩慢的函數計時,而不用測量其餘的程式碼。為此,我們可以使用一個簡單的裝飾器:
(完整程式碼 在這裡)
然後可以將此裝飾器應用於待測功能,如下圖所示:
這給出我們以下輸出:
需要考慮的一件事是我們實際想要測量的時間。時間包提供 time.perf_counter 和 time.process_time 兩個函數。他們的區別在於 perf_counter 返回的絶對值,包括你的 Python 程式進程未運行時的時間,因此它可能會受到電腦負載的影響。另一方面,process_time 僅返回用戶時間(不包括系統時間),這僅是你的過程時間。
加速 Python 的 6 種方式,特定情況可將速度提升 30%
讓 Python 程式運行得更快,這部分會很有趣。我不會展示可以解決你的性能問題的技巧和程式碼,更多地是關於構想和策略的,這些構想和策略在使用時可能會對性能產生巨大影響,在某些情況下,可以將速度提高 30%。
▌使用內置數據類型
這一點很明顯。內置數據類型非常快,尤其是與我們的自定義類型(例如樹或連結列表)相比。這主要是因為內置程式是用 C 實現的,因此在使用 Python 進行編碼時,我們的速度實在無法與之匹敵。
▌使用 lru_cache 緩存/記憶
我已經在上一篇 文章 中展示了此內容,但我認為值得用簡單的範例來重複它:
上面的函數使用 time.sleep 模擬大量計算。第一次使用參數 1 調用時,它將等待 2 秒鐘,然後才返回結果。再次調用時,結果已經被緩存,因此它將跳過函數的主體並立即返回結果。
▌使用局部變數
這與在每個作用域中查詢變數的速度有關,因為它不只是使用局部變數還是全局變數。實際上,即使在函數的局部變數(最快)、類級屬性(例如 self.name,較慢)和全局(例如導入的函數,如 time.time,最慢)之間,查詢速度實際上也有所不同。
你可以透過使用看似不必要的分配來提高性能,如下所示:
▌使用函數
這似乎違反直覺,因為調用函數會將更多的東西放到堆疊上,並從函數返回中產生開銷,但這與上一點有關。如果僅將整個程式碼放在一個文件中,而不將其放入函數中,則由於全局變數,它的運行速度會慢得多。因此,你可以透過將整個程式碼包裝在 main 函數中,並調用一次來加速程式碼,如下所示:
▌不訪問屬性
可能會使你的程式變慢的另一件事是點運算符號(.),它在獲得對象屬性時被使用。此運算符號使用 __getattribute__ 觸發字典查詢,這會在程式碼中產生額外的開銷。那麼,我們如何才能真正避免(限制)使用它呢?
▌當心字元串
使用模數(% s)或 .format()進行循環運行時,字元串操作可能會變得非常慢。我們有什麼更好的選擇?根據雷蒙德.海廷格(Raymond Hettinger)最近的推特,我們唯一應該使用的是 f 字元串,它是最易讀,最簡潔且最快的方法。根據該推特,這是你可以使用的方法列表——最快到最慢:
生成器本質上並沒有更快,因為它們被允許進行延遲計算,從而節省了記憶體而不是時間。但是,保存的記憶體可能會導致你的程式實際運行得更快。這是怎麼做到的?如果你有一個很大的數據集,而沒有使用生成器(疊代器),那麼數據可能會溢出 CPU L1 緩存,這將大大減慢記憶體中值的查詢速度。
在性能方面,非常重要的一點是 CPU 可以將正在處理的所有數據儘可能地保存在緩存中。你可以觀看 Raymond Hettingers 的影片,他在其中提到了這些問題。
優化的首要規則是不要優化。但是,如果確實需要,那麼我希望上面這些技巧可以幫助你。但是,在優化程式碼時要小心,因為它可能最終使你的程式碼難以閲讀,因此難以維護,這可能超過優化的好處。
留言
張貼留言