提高python代碼運(yùn)行效率的一些建議
1. 優(yōu)化代碼和算法
一定要先好好看看你的代碼和算法。許多速度問(wèn)題可以通過(guò)實(shí)現(xiàn)更好的算法或添加緩存來(lái)解決。本文所述都是關(guān)于這一主題的,但要遵循的一些一般指導(dǎo)方針是:
- 測(cè)量,不要猜測(cè)。 測(cè)量代碼中哪些部分運(yùn)行時(shí)間最長(zhǎng),先把重點(diǎn)放在那些部分上。
- 實(shí)現(xiàn)緩存。 如果你從磁盤(pán)、網(wǎng)絡(luò)和數(shù)據(jù)庫(kù)執(zhí)行多次重復(fù)的查找,這可能是一個(gè)很大的優(yōu)化之處。
- 重用對(duì)象,而不是在每次迭代中創(chuàng)建一個(gè)新對(duì)象。Python 必須清理你創(chuàng)建的每個(gè)對(duì)象才能釋放內(nèi)存,這就是所謂的“垃圾回收”。許多未使用對(duì)象的垃圾回收會(huì)大大降低軟件速度。
- 盡可能減少代碼中的迭代次數(shù),特別是減少迭代中的操作次數(shù)。
- 避免(深度)遞歸。 對(duì)于 Python 解釋器來(lái)說(shuō),它需要大量的內(nèi)存和維護(hù)(Housekeeping)。改用生成器和迭代之類的工具。
- 減少內(nèi)存使用。 一般來(lái)說(shuō),盡量減少內(nèi)存的使用。例如,對(duì)一個(gè)巨大的文件進(jìn)行逐行解析,而不是先將其加載到內(nèi)存中。
- 不要這樣做。 聽(tīng)起來(lái)很傻是吧?但是你真的需要執(zhí)行這個(gè)操作嗎?不能晚點(diǎn)兒再執(zhí)行嗎?或者可以只執(zhí)行一次,并且它的結(jié)果可以存儲(chǔ)起來(lái),而不是一遍又一遍地反復(fù)計(jì)算?
2. 使用 PyPy
你可能正在使用 Python 的參考實(shí)現(xiàn) CPython。之所以稱為 CPython,是因?yàn)樗怯?C 語(yǔ)言編寫(xiě)的。如果你確定你的代碼是 CPU 密集型(CPU bound)(如果你不知道這一術(shù)語(yǔ),請(qǐng)參見(jiàn)本文“使用線程”一節(jié))的話,那么你應(yīng)該研究一下 PyPy,它是 CPython 的替代方案。這可能是一種快速解決方案,無(wú)需更改任何一行代碼。
PyPy 聲稱,它的平均速度比 CPython 要快 4.4 倍。它是通過(guò)使用一種稱為 Just-in-time(JIT,即時(shí)編譯)技術(shù)來(lái)實(shí)現(xiàn)的。Java 和 .NET 框架就是 JIT 編譯的其他著名的例子。相比之下,CPython 使用解釋來(lái)執(zhí)行代碼。雖然這一做法提供了很大的靈活性,但速度也變得慢了下來(lái)。
使用 JIT,你的代碼是在運(yùn)行程序時(shí)即時(shí)編譯的。它結(jié)合了 Ahead-of-time(AOT,提前編譯)技術(shù)的速度優(yōu)勢(shì)(由 C 和 C++ 等語(yǔ)言使用)和解釋的靈活性。另一個(gè)優(yōu)點(diǎn)是 JIT 編譯器可以在運(yùn)行時(shí)不斷優(yōu)化代碼。代碼運(yùn)行的時(shí)間越長(zhǎng),它就會(huì)變得越優(yōu)化。
PyPy 在過(guò)去幾年中取得了長(zhǎng)足的進(jìn)步,通常情況下,它可以作為 Python 2 和 Python 3 的簡(jiǎn)易替換方案。使用 Pipenv 這樣的工具,它也可以完美地工作,試試看吧!
3. 使用線程
大部分軟件都是 IO 密集型,而不是 CPU 密集型。如果你對(duì)這些術(shù)語(yǔ)還不熟悉的話,請(qǐng)看看下面的解釋:
- IO 密集型(I/O bound):軟件主要是等待輸入 / 輸出操作完成才能工作。在從網(wǎng)絡(luò)或緩慢的存儲(chǔ)中獲取數(shù)據(jù)時(shí),通常會(huì)出現(xiàn)這種情況。
- CPU 密集型(CPU bound):軟件占用了大量的 CPU 資源。它使用了 CPU 所有的能力來(lái)產(chǎn)生所需的結(jié)果。
在等待來(lái)自網(wǎng)絡(luò)或磁盤(pán)的應(yīng)答時(shí),你可以使用多個(gè)線程使其他部分保持運(yùn)行狀態(tài)。
一個(gè)線程是一個(gè)獨(dú)立的執(zhí)行序列。默認(rèn)情況下,Python 程序有一個(gè)主線程。但你可以創(chuàng)建更多的主線程,并讓 Python 在它們之間切換。這種切換發(fā)生得如此之快,以至于它們看上去就好像是在同時(shí)并排運(yùn)行一樣。
但與其他編程語(yǔ)言不同的是,Python 并不是同時(shí)運(yùn)行的,而是輪流運(yùn)行。這是因?yàn)?Python 中有一種全局解釋器鎖( Global Interpreter Lock,GIL)機(jī)制。這一點(diǎn),以及 threading 庫(kù)在 我撰寫(xiě)的關(guān)于 Python 并發(fā)性的文章 有詳細(xì)的解釋。
我們得到的結(jié)論是,線程對(duì)于 IO 密集型的軟件有很大的影響,但對(duì) CPU 密集型的軟件毫無(wú)用處。
這是為什么呢?很簡(jiǎn)單。當(dāng)一個(gè)線程在等待來(lái)自網(wǎng)絡(luò)的答復(fù)時(shí),其他線程可以繼續(xù)運(yùn)行。如果你要執(zhí)行大量的網(wǎng)絡(luò)請(qǐng)求,線程可以帶來(lái)巨大的差異。如果你的線程正在進(jìn)行繁重的計(jì)算,那么它們只是等待輪到它們繼續(xù)計(jì)算,線程化只會(huì)帶來(lái)更多的開(kāi)銷。
4. 使用 Asyncio
Asyncio 是 Python 中一個(gè)相對(duì)較新的核心庫(kù)。它解決了與線程相同的問(wèn)題:它加快了 IO 密集型軟件的速度,但這是以不同的方式實(shí)現(xiàn)的。我將立即坦承我并非 Python 的 asyncio 擁躉。它相當(dāng)復(fù)雜,特別是對(duì)于初學(xué)者來(lái)說(shuō)。我遇到的另一個(gè)問(wèn)題是, asyncio 庫(kù)在過(guò)去幾年中有了很大的發(fā)展。網(wǎng)上的教程和示例代碼常常已經(jīng)過(guò)時(shí)。不過(guò),這并不意味著它就毫無(wú)用處。
5 同時(shí)使用多個(gè)處理器
如果你的軟件是 CPU 密集型的,你通??梢杂靡环N可以同時(shí)使用更多處理器的方式重寫(xiě)你的代碼。通過(guò)這種方式,你就可以線性地調(diào)整執(zhí)行速度。
這就是所謂的并行性,但并不是所有的算法都可以并行運(yùn)行。例如,簡(jiǎn)單的將遞歸算法進(jìn)行并行化是不可能的。但是幾乎總有一種替代算法可以很好地并行工作。
使用更多處理處理器有兩種方式:
- 在同一臺(tái)機(jī)器內(nèi)使用多個(gè)處理器和 / 或內(nèi)核。在 Python 中,這可以通過(guò) multiprocessing 庫(kù)來(lái)完成。
- 使用計(jì)算機(jī)網(wǎng)絡(luò)來(lái)使用多個(gè)處理器,分布在多臺(tái)計(jì)算機(jī)上。我們稱之為分布式計(jì)算。
這篇 關(guān)于 Python 并發(fā)性的文章 側(cè)重于介紹如何在一臺(tái)機(jī)器的范圍內(nèi)擴(kuò)展 Python 軟件的方法。它還介紹了 multiprocessing 庫(kù)。如果你認(rèn)為這是你需要的資料,一定要去看看。
與 threading 庫(kù)不同, multiprocessing 庫(kù)繞過(guò)了 Python 的全局解釋器鎖。它實(shí)際上是通過(guò)派生多個(gè) Python 實(shí)例來(lái)實(shí)現(xiàn)這一點(diǎn)的。因此,現(xiàn)在你可以讓多個(gè) Python 進(jìn)程同時(shí)運(yùn)行你的代碼,而不是在單個(gè) Python 進(jìn)程中輪流運(yùn)行線程。
multiprocessing 庫(kù)和 threading 庫(kù)非常相似??赡艹霈F(xiàn)的問(wèn)題是:為什么還要考慮線程呢?答案是可以猜得到的。線程是“輕量”的:它需要更少的內(nèi)存,因?yàn)樗恍枰粋€(gè)正在運(yùn)行的 Python 解釋器。產(chǎn)生新進(jìn)程也還有其開(kāi)銷。因此,如果你的代碼是 IO 密集型的,線程可能就足夠好了。
一旦你實(shí)現(xiàn)了軟件的并行工作,那么在使用 Hadoop 之類的分布式計(jì)算方面就前進(jìn)了一小步。通過(guò)利用云計(jì)算平臺(tái),你可以相對(duì)輕松地進(jìn)行擴(kuò)展規(guī)模。例如,你可以在云端中處理大型數(shù)據(jù)集,并在本地使用結(jié)果。使用混合操作的方式,你可以節(jié)省一些資金,因?yàn)樵贫酥械乃懔Ψ浅0嘿F。
總結(jié)
總結(jié)起來(lái)就是:
- 首先考慮優(yōu)化你的算法和代碼。
- 如果原始速度可以解決你的問(wèn)題,請(qǐng)考慮使用 PyPy。
- 對(duì) IO 密集型軟件使用 threading 庫(kù)和 asyncio 。
- 使用 multiprocessing 庫(kù)解決 CPU 密集型問(wèn)題。
- 如果所有這些措施還不夠的話,可以利用 Hadoop 等云計(jì)算平臺(tái)進(jìn)行擴(kuò)展規(guī)模。
以上就是提高python代碼運(yùn)行效率的一些建議的詳細(xì)內(nèi)容,更多關(guān)于提高python代碼運(yùn)行效率的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Pygame實(shí)戰(zhàn)練習(xí)之炸彈人學(xué)院游戲
炸彈人學(xué)院想必是很多人童年時(shí)期的經(jīng)典游戲,我們依舊能記得抱個(gè)老人機(jī)娛樂(lè)的場(chǎng)景,下面這篇文章主要給大家介紹了關(guān)于如何利用python寫(xiě)一個(gè)簡(jiǎn)單的炸彈人學(xué)院小游戲的相關(guān)資料,需要的朋友可以參考下2021-09-09pandas.dataframe中根據(jù)條件獲取元素所在的位置方法(索引)
今天小編就為大家分享一篇pandas.dataframe中根據(jù)條件獲取元素所在的位置方法(索引),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-06-06Python?FastApi結(jié)合異步執(zhí)行方式
這篇文章主要介紹了Python?FastApi結(jié)合異步執(zhí)行方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06Anaconda之conda常用命令介紹(安裝、更新、刪除)
這篇文章主要介紹了Anaconda之conda常用命令介紹,主要包括安裝、更新、刪除等,需要的朋友可以參考下2019-10-10Python cookbook(數(shù)據(jù)結(jié)構(gòu)與算法)從序列中移除重復(fù)項(xiàng)且保持元素間順序不變的方法
這篇文章主要介紹了Python cookbook(數(shù)據(jù)結(jié)構(gòu)與算法)從序列中移除重復(fù)項(xiàng)且保持元素間順序不變的方法,涉及Python針對(duì)列表與字典的元素遍歷、判斷、去重、排序等相關(guān)操作技巧,需要的朋友可以參考下2018-03-03python使用BeautifulSoup分析網(wǎng)頁(yè)信息的方法
這篇文章主要介紹了python使用BeautifulSoup分析網(wǎng)頁(yè)信息的方法,涉及Python使用BeautifulSoup模塊分析網(wǎng)頁(yè)信息的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04解決Jupyter Notebook “signal only works&nb
這篇文章主要介紹了解決Jupyter Notebook “signal only works in main thread“問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01Python?中?Selenium?的?send_keys()?函數(shù)用法小結(jié)
send_keys() 是將數(shù)字、文本和符號(hào)等鍵盤(pán)輸入發(fā)送到應(yīng)用程序的文本框的過(guò)程, send_keys() 是 WebDriver 的一部分,每個(gè)鍵盤(pán)輸入都會(huì)發(fā)送到此元素,這篇文章主要介紹了Python?中?Selenium?的?send_keys()?函數(shù),需要的朋友可以參考下2023-11-11Python ValueError: invalid literal for int() with base 10 實(shí)用
這篇文章主要介紹了Python ValueError: invalid literal for int() with base 10 實(shí)用解決方法,本文使用了一個(gè)取巧方法解決了這個(gè)問(wèn)題,需要的朋友可以參考下2015-06-06