討厭Python的人總是說,他們不想使用它的原因之一是它很 慢。嗯,特定程序(無論使用何種編程語言)是快還是慢,在很大程度上取決于編寫該程序的開發人員以及編寫優化而 快速的 程序的技能和能力 。
因此,讓我們證明一些人是錯誤的,讓我們看看如何改善Python程序的性能 并使它們真正更快!
時序分析
在開始進行任何優化之前,我們首先需要找出代碼的哪些部分實際上會使整個程序變慢。有時程序的瓶頸可能很明顯,但是如果您不知道它在哪里,那么這里有一些可供您選擇的選項:
最懶惰的“配置文件”
首先,最簡單和誠實的說是非常懶惰的解決方案-Unix time
命令:
如果您只想計時整個程序,這可能會起作用,通常這是不夠的……
最詳細的分析
另一端是 cProfile,它將為您提供 過多 信息:
在這里,我們使用cProfile模塊和 time 參數運行測試腳本 ,以便按內部時間(cumtime)對行進行排序 。這給了我們 很多信息,您在上面看到的行大約是實際輸出的10%。由此可見, exp 函數是罪魁禍首( Surprise,Surprise),現在我們可以更詳細地了解時序和性能分析...
時序特定功能
現在我們知道了將注意力轉移到哪里,我們可能想對慢速函數計時,而不用測量其余的代碼。為此,我們可以使用簡單的裝飾器:
然后可以將此裝飾器應用于待測功能,如下所示:
這給我們這樣的輸出:
有一點要考慮的是 什么樣的時間,我們其實(想)措施。時間包提供
time.perf_counter 和 time.process_time。此處的區別是perf_counter返回絕對值,其中包括Python程序進程未運行時的時間,因此它可能會受到計算機負載的影響。另一方面,process_time僅返回用戶時間(不包括系統時間),這僅是您的處理時間。
使其更快
現在是有趣的部分。讓我們讓您的Python程序運行得更快。我(大部分)不會向您展示一些可以神奇地解決您的性能問題的技巧,技巧和代碼段。這更多地是關于一般構想和策略的,這些構想和策略在使用時可能會對性能產生巨大影響,在某些情況下,最高可以提高30%。
使用內置數據類型
這個很明顯。內置數據類型非常快,特別是與樹或鏈接列表之類的自定義類型相比。這主要是因為內置程序是用C實現的 ,因此在使用Python進行編碼時我們的速度實在無法與之匹敵。
使用lru_cache緩存/記憶
使用局部變量
這與在每個作用域中查找變量的速度有關。我正在編寫 每個作用域,因為它不只是使用局部變量還是全局變量。實際上,即使在函數(最快),類級屬性(例如self.name ,較慢)和全局變量(例如,最慢)等全局 變量之間,查找速度實際上也有所不同 time.time 。您可以通過使用看似不必要(直接無用的)的分配來提高性能,如下所示:
使用函數
這似乎違反直覺,因為調用函數會將更多的東西放到堆棧上,并從函數返回中產生開銷,但這與上一點有關。如果僅將整個代碼放在一個文件中而不將其放入函數中,則由于全局變量,它的運行速度會慢得多。因此,您可以通過將整個代碼包裝在main函數中并調用一次來加速代碼 ,如下所示:
不訪問屬性
可能會使程序變慢的另一件事是 點運算符(.),在訪問對象屬性時使用。該運算符使用觸發字典查找 __getattribute__,這會在代碼中產生額外的開銷。那么,我們如何才能真正避免(限制)使用它呢?
當心字符串
當使用模數 (%s)或 .format()。進行循環運行時,字符串操作可能會變得非常慢 。我們有什么更好的選擇?我們唯一應該使用的是 f-string,它是最易讀,簡潔且最快的方法。因此,根據該推文,這是您可以使用的方法列表-最快到最慢:
生成器本質上并沒有更快,因為它們被允許進行惰性計算,從而節省了內存而不是時間。但是,保存的內存可能會導致您的程序實際運行得更快。怎么樣?好吧,如果您有一個很大的數據集,并且沒有使用生成器(迭代器),那么數據可能會溢出CPU L1緩存,這將大大減慢內存中值的查找。
就性能而言,非常重要的一點是CPU可以將正在處理的所有數據盡可能地保存在緩存中。
結論
優化的首要規則是 不這樣做。但是,如果確實需要,那么我希望這些技巧可以幫助您。但是,在優化代碼時要小心,因為它可能最終使您的代碼難以閱讀,因此難以維護,這可能會超出優化的好處。