Python慢,這幾種是常見的原因:“因為它是GIL(全局解釋器鎖)”,“因為它是解釋語言不是編譯語言”,“因為它是動態類型語言”。
推薦課程:Java教學。
究竟哪個原因對效能的影響最大?
「因為它是GIL」
現代電腦的 CPU 有多個核心,有時甚至有多個處理器。為了利用所有運算能力,作業系統定義了一個底層結構,叫做線程,而一個進程(例如 Chrome瀏覽器)能夠產生多個線程,透過線程來執行系統指令。這樣如果一個行程是要使用很多 CPU,那麼運算負載就會由多個核心分擔,最終使得絕大多數應用程式能更快完成任務。
在撰寫本文時,我的 Chrome 瀏覽器開了 44 個執行緒。另外,基於 POSIX 的作業系統(如 Mac OS 和 Linux)的執行緒結構和 API 與 Windows 作業系統是不一樣的。作業系統也負責線程的調度。
如果你沒寫過多線程程序,那麼你應該了解一下鎖的概念。與單執行緒進程不同,在多執行緒程式設計中,你要確保改變記憶體中的變數時,多個執行緒不會試圖同時修改或存取同一個記憶體位址。
CPython 在建立變數時會分配內存,然後用一個計數器計算對該變數的引用的次數。這個概念叫做「引用計數」。如果引用的數目為 0,那就可以將這個變數從系統中釋放掉。這樣,建立「臨時」變數(如在 for 迴圈的上下文環境中)不會耗光應用程式的記憶體。
隨之而來的問題是,如果變數在多個執行緒中共享,CPython 需要對引用計數器加鎖。有一個「全域解釋器鎖」會謹慎地控制執行緒的執行。不管有多少個線程,解釋器一次只能執行一個操作。
這對 Python 應用的效能有什麼影響?
如果應用程式是單執行緒、單解釋器的,那麼這不會對速度有任何影響。去掉 GIL 也不會影響程式碼的效能。
但如果想用一個解釋器(一個Python 進程)透過線程實現並發,而且線程是IO 密集型的(即有很多網路輸入輸出或磁碟輸入輸出),那麼就會出現下面這種GIL 競爭:
如果Web 應用程式(如Django)使用了WSGI,那麼發送到Web 應用程式的每個請求都會由獨立的Python 解譯器執行,因此每個請求只會有一個鎖。由於 Python 解釋器啟動很慢,一些 WSGI 實作就支援“守護模式”,保持 Python 進程長期運行。
「因為它是解釋語言」
這個理由我也聽過很多,我發現它過於簡化了 CPython 的實際運作原理。當你在終端機上寫 python myscript.py 時,CPython 會啟動一長串操作,包括讀取、字法分析、語法分析、編譯、解釋以及執行。個過程的重點就是它會在編譯階段產生.pyc文件,字節碼會寫到__pycache__/下的文件(如果是Python 3),或寫到與原始碼同一個目錄(Python 2)。不僅你寫的腳本是這樣,所有你導入的程式碼都是這樣,包括第三方模組。
因此絕大多數情況下(除非你寫的程式碼只會運行一次),Python是在解釋字節碼並在本地執行。與Java和C#.NET比較一下:
Java將原始程式碼編譯成“中間語言”,然後Java虛擬機讀取字節碼並即時編譯成機器碼。 .NET CIL也是一樣的,.NET的公共語言執行時期(CLR)使用即時編譯將字節碼編譯成機器碼。
那麼,既然它們都使用虛擬機,以及某種字節碼,為什麼Python在效能測試中比Java和C#慢那麼多?第一個原因是,.NET和Java是即時編譯的(JIT)。
即時編譯,即JIT(Just-in-time),需要一種中間語言,將程式碼分割成小塊(或稱幀)。而提前編譯(Ahead of Time,簡稱AOT)是編譯器把原始碼翻譯成CPU能理解的程式碼之後再執行。
JIT本身並不能讓執行更快,因為它執行的是同樣的字節碼序列。但是,JIT可以在運行時做出最佳化。好的GIT優化器能找到應用程式中執行最多的部分,稱為「熱點」。然後對那些字節碼進行最佳化,將它們替換成效率更高的程式碼。
這就是說,如果你的應用程式會反覆做某件事情,那麼速度就會快很多。此外,別忘了Java和C#都是強型別語言,所以優化器可以對程式碼做更多的假設。
「因為它是動態型別語言」
#「靜態類型」語言要求必須在變數定義時指定其類型,例如C、C 、Java、C#和Go等。
而動態型別語言中儘管也有型別的概念,但變數的型別是動態的。
a = 1 a = "foo"
在這個例子中,Python用相同的名字和str類型定義了第二個變量,同時釋放了第一個a的實例佔用的記憶體。
靜態型別語言的設計目的並不是折磨人,這樣設計是因為CPU就是這樣運作的。如果任何操作最終都要轉換成簡單的二進位操作,那就需要將物件和類型轉換成低階資料結構。
Python幫你做了這一切,只不過你從來沒有關心過,也不需要關心。
不需要定義類型並不是Python慢的原因。 Python的設計可以讓你把一切都做成動態的。你可以在運行時替換物件的方法,可以在運行時為底層系統呼叫打補丁。幾乎一切都有可能。
而這個設計使得Python的最佳化變得很困難。
那麼,Python的動態類型是否讓Python更慢?
比較並轉換類型的代價很大。每次讀取、寫入或引用變臉時都會檢查類型
動態類型的語言很難最佳化。許多替代Python的語言很快的原因就是它們犧牲了便利性來交換表現。
例如Cython(http://cython.org/),它透過結合C的靜態類型和Python的方式,使得程式碼中的類型已知,從而優化程式碼,能夠獲得84倍的性能提升
總結
Python慢的主要原因是因為它的動態和多樣性。它能用於解決各種問題,但多數問題都有優化得更好、更快的解決方案。
但Python應用也有許多最佳化措施,如使用非同步、理解效能測試工具,以及使用多重解釋器等。
對於啟動時間不重要,而程式碼可能享受到JIT的好處的應用,可以考慮使用PyPy。
對於程式碼中效能很重要的部分,如果變數大多是靜態類型,可以考慮使用Cython。
以上是java為什麼比python快的詳細內容。更多資訊請關注PHP中文網其他相關文章!