真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Python為什么這么慢

本篇內(nèi)容介紹了“Python為什么這么慢”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

我們提供的服務(wù)有:成都網(wǎng)站建設(shè)、成都做網(wǎng)站、微信公眾號(hào)開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、肅南裕固族自治ssl等。為上千家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的肅南裕固族自治網(wǎng)站制作公司

Python 現(xiàn)在越來越火,已經(jīng)迅速擴(kuò)張到包括 DevOps、數(shù)據(jù)科學(xué)、Web 開發(fā)、信息安全等各個(gè)領(lǐng)域當(dāng)中。

然而,相比起 Python 擴(kuò)張的速度,Python 代碼的運(yùn)行速度就顯得有點(diǎn)遜色了。

在代碼運(yùn)行速度方面,Java、C、C++、C# 和 Python 要如何進(jìn)行比較呢?并沒有一個(gè)放之四海而皆準(zhǔn)的標(biāo)準(zhǔn),因?yàn)榫唧w結(jié)果很大程度上取決于運(yùn)行的程序類型,而語(yǔ)言基準(zhǔn)測(cè)試Computer Language Benchmarks Games可以作為衡量的一個(gè)方面。

根據(jù)我這些年來進(jìn)行語(yǔ)言基準(zhǔn)測(cè)試的經(jīng)驗(yàn)來看,Python 比很多語(yǔ)言運(yùn)行起來都要慢。無(wú)論是使用 JIT 編譯器的 C#、Java,還是使用 AOT 編譯器的 C、C++,又或者是 JavaScript 這些解釋型語(yǔ)言,Python 都比它們運(yùn)行得慢。

注意:對(duì)于文中的 “Python” ,一般指 CPython 這個(gè)官方的實(shí)現(xiàn)。當(dāng)然我也會(huì)在本文中提到其它語(yǔ)言的 Python 實(shí)現(xiàn)。

我要回答的是這個(gè)問題:對(duì)于一個(gè)類似的程序,Python 要比其它語(yǔ)言慢 2 到 10 倍不等,這其中的原因是什么?又有沒有改善的方法呢?

主流的說法有這些:

  • “是

    全局解釋器鎖Global Interpreter Lock

    (GIL)的原因”

  • “是因?yàn)?Python 是解釋型語(yǔ)言而不是編譯型語(yǔ)言”

  • “是因?yàn)?Python 是一種動(dòng)態(tài)類型的語(yǔ)言”

哪一個(gè)才是是影響 Python 運(yùn)行效率的主要原因呢?

是全局解釋器鎖的原因嗎?

現(xiàn)在很多計(jì)算機(jī)都配備了具有多個(gè)核的 CPU  ,有時(shí)甚至還會(huì)有多個(gè)處理器。為了更充分利用它們的處理能力,操作系統(tǒng)定義了一個(gè)稱為線程的低級(jí)結(jié)構(gòu)。某一個(gè)進(jìn)程(例如 Chrome  瀏覽器)可以建立多個(gè)線程,在系統(tǒng)內(nèi)執(zhí)行不同的操作。在這種情況下,CPU  密集型進(jìn)程就可以跨核心分擔(dān)負(fù)載了,這樣的做法可以大大提高應(yīng)用程序的運(yùn)行效率。

例如在我寫這篇文章時(shí),我的 Chrome 瀏覽器打開了 44 個(gè)線程。需要提及的是,基于 POSIX 的操作系統(tǒng)(例如 Mac OS、Linux)和 Windows 操作系統(tǒng)的線程結(jié)構(gòu)、API 都是不同的,因此操作系統(tǒng)還負(fù)責(zé)對(duì)各個(gè)線程的調(diào)度。

如果你還沒有寫過多線程執(zhí)行的代碼,你就需要了解一下線程鎖的概念了。多線程進(jìn)程比單線程進(jìn)程更為復(fù)雜,是因?yàn)樾枰褂镁€程鎖來確保同一個(gè)內(nèi)存地址中的數(shù)據(jù)不會(huì)被多個(gè)線程同時(shí)訪問或更改。

CPython 解釋器在創(chuàng)建變量時(shí),首先會(huì)分配內(nèi)存,然后對(duì)該變量的引用進(jìn)行計(jì)數(shù),這稱為引用計(jì)數(shù)reference counting。如果變量的引用數(shù)變?yōu)?0,這個(gè)變量就會(huì)從內(nèi)存中釋放掉。這就是在 for 循環(huán)代碼塊內(nèi)創(chuàng)建臨時(shí)變量不會(huì)增加內(nèi)存消耗的原因。

而當(dāng)多個(gè)線程內(nèi)共享一個(gè)變量時(shí),CPython 鎖定引用計(jì)數(shù)的關(guān)鍵就在于使用了 GIL,它會(huì)謹(jǐn)慎地控制線程的執(zhí)行情況,無(wú)論同時(shí)存在多少個(gè)線程,解釋器每次只允許一個(gè)線程進(jìn)行操作。

這會(huì)對(duì) Python 程序的性能有什么影響?

如果你的程序只有單線程、單進(jìn)程,代碼的速度和性能不會(huì)受到全局解釋器鎖的影響。

但如果你通過在單進(jìn)程中使用多線程實(shí)現(xiàn)并發(fā),并且是 IO 密集型(例如網(wǎng)絡(luò) IO 或磁盤 IO)的線程,GIL 競(jìng)爭(zhēng)的效果就很明顯了。

Python為什么這么慢

由 David Beazley 提供的 GIL 競(jìng)爭(zhēng)情況圖http://dabeaz.blogspot.com/2010/01/python-gil-visualized.html

對(duì)于一個(gè) web 應(yīng)用(例如 Django),同時(shí)還使用了 WSGI,那么對(duì)這個(gè) web 應(yīng)用的每一個(gè)請(qǐng)求都運(yùn)行一個(gè)單獨(dú)的 Python 解釋器,而且每個(gè)請(qǐng)求只有一個(gè)鎖。同時(shí)因?yàn)?Python 解釋器的啟動(dòng)比較慢,某些 WSGI 實(shí)現(xiàn)還具有“守護(hù)進(jìn)程模式”,可以使 Python 進(jìn)程一直就緒。

其它的 Python 解釋器表現(xiàn)如何?

PyPy 也是一種帶有 GIL 的解釋器,但通常比 CPython 要快 3 倍以上。

Jython 則是一種沒有 GIL 的解釋器,這是因?yàn)?Jython 中的 Python 線程使用 Java 線程來實(shí)現(xiàn),并且由 JVM 內(nèi)存管理系統(tǒng)來進(jìn)行管理。

JavaScript 在這方面又是怎樣做的呢?

所有的 Javascript 引擎使用的都是 mark-and-sweep 垃圾收集算法,而 GIL 使用的則是 CPython 的內(nèi)存管理算法。

JavaScript 沒有 GIL,而且它是單線程的,也不需要用到 GIL, JavaScript 的事件循環(huán)和 Promise/Callback 模式實(shí)現(xiàn)了以異步編程的方式代替并發(fā)。在 Python 當(dāng)中也有一個(gè)類似的 asyncio 事件循環(huán)。

是因?yàn)?Python 是解釋型語(yǔ)言嗎?

我經(jīng)常會(huì)聽到這個(gè)說法,但是這過于粗陋地簡(jiǎn)化了 Python 所實(shí)際做的工作了。其實(shí)當(dāng)終端上執(zhí)行 python myscript.py 之后,CPython 會(huì)對(duì)代碼進(jìn)行一系列的讀取、語(yǔ)法分析、解析、編譯、解釋和執(zhí)行的操作。

如果你對(duì)這一系列過程感興趣,也可以閱讀一下我之前的文章:在 6 分鐘內(nèi)修改 Python 語(yǔ)言 。

.pyc 文件的創(chuàng)建是這個(gè)過程的重點(diǎn)。在代碼編譯階段,Python 3 會(huì)將字節(jié)碼序列寫入 __pycache__/ 下的文件中,而 Python 2 則會(huì)將字節(jié)碼序列寫入當(dāng)前目錄的 .pyc 文件中。對(duì)于你編寫的腳本、導(dǎo)入的所有代碼以及第三方模塊都是如此。

因此,絕大多數(shù)情況下(除非你的代碼是一次性的……),Python 都會(huì)解釋字節(jié)碼并本地執(zhí)行。與 Java、C#.NET 相比:

Java 代碼會(huì)被編譯為“中間語(yǔ)言”,由 Java 虛擬機(jī)讀取字節(jié)碼,并將其即時(shí)編譯為機(jī)器碼。.NET CIL 也是如此,.NET CLR(Common-Language-Runtime)將字節(jié)碼即時(shí)編譯為機(jī)器碼。

既然 Python 像 Java 和 C# 那樣都使用虛擬機(jī)或某種字節(jié)碼,為什么 Python 在基準(zhǔn)測(cè)試中仍然比 Java 和 C# 慢得多呢?首要原因是,.NET 和 Java 都是 JIT 編譯的。

即時(shí)Just-in-time(JIT)編譯需要一種中間語(yǔ)言,以便將代碼拆分為多個(gè)塊(或多個(gè)幀)。而提前ahead of time(AOT)編譯器則需要確保 CPU 在任何交互發(fā)生之前理解每一行代碼。

JIT 本身不會(huì)使執(zhí)行速度加快,因?yàn)樗鼒?zhí)行的仍然是同樣的字節(jié)碼序列。但是 JIT 會(huì)允許在運(yùn)行時(shí)進(jìn)行優(yōu)化。一個(gè)優(yōu)秀的 JIT 優(yōu)化器會(huì)分析出程序的哪些部分會(huì)被多次執(zhí)行,這就是程序中的“熱點(diǎn)”,然后優(yōu)化器會(huì)將這些代碼替換為更有效率的版本以實(shí)現(xiàn)優(yōu)化。

這就意味著如果你的程序是多次重復(fù)相同的操作時(shí),有可能會(huì)被優(yōu)化器優(yōu)化得更快。而且,Java 和 C# 是強(qiáng)類型語(yǔ)言,因此優(yōu)化器對(duì)代碼的判斷可以更為準(zhǔn)確。

PyPy 使用了明顯快于 CPython 的 JIT。更詳細(xì)的結(jié)果可以在這篇性能基準(zhǔn)測(cè)試文章中看到:哪一個(gè) Python 版本最快?。

那為什么 CPython 不使用 JIT 呢?

JIT 也不是***的,它的一個(gè)顯著缺點(diǎn)就在于啟動(dòng)時(shí)間。 CPython 的啟動(dòng)時(shí)間已經(jīng)相對(duì)比較慢,而 PyPy 比 CPython  啟動(dòng)還要慢 2 到 3 倍。Java 虛擬機(jī)啟動(dòng)速度也是出了名的慢。.NET CLR 則通過在系統(tǒng)啟動(dòng)時(shí)啟動(dòng)來優(yōu)化體驗(yàn),而 CLR  的開發(fā)者也是在 CLR 上開發(fā)該操作系統(tǒng)。

因此如果你有個(gè)長(zhǎng)時(shí)間運(yùn)行的單一 Python 進(jìn)程,JIT 就比較有意義了,因?yàn)榇a里有“熱點(diǎn)”可以優(yōu)化。

不過,CPython 是個(gè)通用的實(shí)現(xiàn)。設(shè)想如果使用 Python 開發(fā)命令行程序,但每次調(diào)用 CLI 時(shí)都必須等待 JIT 緩慢啟動(dòng),這種體驗(yàn)就相當(dāng)不好了。

CPython 試圖用于各種使用情況。有可能實(shí)現(xiàn)將 JIT 插入到 CPython 中,但這個(gè)改進(jìn)工作的進(jìn)度基本處于停滯不前的狀態(tài)。

如果你想充分發(fā)揮 JIT 的優(yōu)勢(shì),請(qǐng)使用 PyPy。

是因?yàn)?Python 是一種動(dòng)態(tài)類型的語(yǔ)言嗎?

在 C、C++、Java、C#、Go 這些靜態(tài)類型語(yǔ)言中,必須在聲明變量時(shí)指定變量的類型。而在動(dòng)態(tài)類型語(yǔ)言中,雖然也有類型的概念,但變量的類型是可改變的。

a = 1a = "foo"

在上面這個(gè)示例里,Python 將變量 a 一開始存儲(chǔ)整數(shù)類型變量的內(nèi)存空間釋放了,并創(chuàng)建了一個(gè)新的存儲(chǔ)字符串類型的內(nèi)存空間,并且和原來的變量同名。

靜態(tài)類型語(yǔ)言這樣的設(shè)計(jì)并不是為了為難你,而是為了方便 CPU 運(yùn)行而這樣設(shè)計(jì)的。因?yàn)樽罱K都需要將所有操作都對(duì)應(yīng)為簡(jiǎn)單的二進(jìn)制操作,因此必須將對(duì)象、類型這些高級(jí)的數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換為低級(jí)數(shù)據(jù)結(jié)構(gòu)。

Python 也實(shí)現(xiàn)了這樣的轉(zhuǎn)換,但用戶看不到這些轉(zhuǎn)換,也不需要關(guān)心這些轉(zhuǎn)換。

不用必須聲明類型并不是為了使 Python 運(yùn)行慢,Python 的設(shè)計(jì)是讓用戶可以讓各種東西變得動(dòng)態(tài):可以在運(yùn)行時(shí)更改對(duì)象上的方法,也可以在運(yùn)行時(shí)動(dòng)態(tài)添加底層系統(tǒng)調(diào)用到值的聲明上,幾乎可以做到任何事。

但也正是這種設(shè)計(jì)使得 Python 的優(yōu)化異常的難。

為了證明我的觀點(diǎn),我使用了一個(gè) Mac OS 上的系統(tǒng)調(diào)用跟蹤工具 DTrace。CPython 發(fā)布版本中沒有內(nèi)置 DTrace,因此必須重新對(duì) CPython 進(jìn)行編譯。以下以 Python 3.6.6 為例:

wget https://github.com/python/cpython/archive/v3.6.6.zipunzip v3.6.6.zipcd v3.6.6./configure --with-dtracemake

這樣 python.exe 將使用 DTrace 追蹤所有代碼。Paul Ross 也作過關(guān)于 DTrace 的閃電演講。你可以下載 Python 的 DTrace 啟動(dòng)文件來查看函數(shù)調(diào)用、執(zhí)行時(shí)間、CPU 時(shí)間、系統(tǒng)調(diào)用,以及各種其它的內(nèi)容。

sudo dtrace -s toolkit/.d -c ‘../cpython/python.exe script.py’

py_callflow 追蹤器顯示了程序里調(diào)用的所有函數(shù)。

那么,Python 的動(dòng)態(tài)類型會(huì)讓它變慢嗎?

  • 類型比較和類型轉(zhuǎn)換消耗的資源是比較多的,每次讀取、寫入或引用變量時(shí)都會(huì)檢查變量的類型

  • Python 的動(dòng)態(tài)程度讓它難以被優(yōu)化,因此很多 Python 的替代品能夠如此快都是為了提升速度而在靈活性方面作出了妥協(xié)

  • 而 Cython 結(jié)合了 C 的靜態(tài)類型和 Python 來優(yōu)化已知類型的代碼,它可以將性能提升 84 倍。

“Python為什么這么慢”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!


文章題目:Python為什么這么慢
地址分享:http://weahome.cn/article/iedoss.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部