Python當前人氣暴漲。它在DevOps,數據科學,Web開發和安全領域均有使用。javascript
可是在速度方面沒有贏得美譽。html
這裏有關於Python比較其餘語言如,Java, C#, Go, JavaScript, C++進行性能對比,其中Python是最慢的。包含了JIT(C#, Java)和AOT(C,C++)編譯器,也有像解釋型語言如JavaScript。java
注意:文章中我所提到的"Python"均指使用C語言實現的CPython。python
爲何要比其餘語言慢到2-10x的速度?git
這是相關緣由:github
那這些緣由中哪一個佔最大成分呢?web
現代計算機大多數都具有多核,有時還有多處理器。爲了充分利用這些處理能力,操做系統底層提供了一個叫作線程的東西,它(例如Chrome瀏覽器)能夠系統內容建立多個線程進行指令處理。也就是說當一個進程是CPU密集型,那就能夠經過多個核協同工做來提升應用的運行速度。算法
我本地Chrome瀏覽器當前會開啓44個線程,在不一樣的操做系統例如POSIX(Mac OS和Linux)和Windows提供的線程API結構不同。操做系統來負責線程的調度。編程
若是你以前並無進行過多線程編程,你須要熟悉一個叫作鎖的概念。不像單線程進行,你須要確保在內存改變一個變量時,多個線程不會同時進行操做。瀏覽器
CPython建立變量時,它會開闢內存,而後計算有多少引用該變量,這個概念叫作引用計數。若是引用技術爲0時,它會將內存釋放回給系統。這也就是建立臨時變量,進行循環操做時並不會耗光內存
在多線程共享變量時,CPython時如何對引用計數上鎖呢。這裏就是「全局解釋鎖(global interpreter lock)」負責作的事情,無論你有多少的線程,解釋器在同一個時間只能有一個線程進行操做。
若是應用是單解釋器,單線程。在速度上沒有任何影響。
若是你想在單解釋器使用線程了實現併發操做,而且它們是IO密集型(例如網絡IO或者硬盤IO),那你將會看到GIL相似以下競爭執行:
如你有一個web應用(例如Django)而且使用WSGI,每一個請求將會分配到單獨Python解釋器,此時一個請求只有一把鎖。由於Python解釋器啓動比較慢,一些WSGI會實現爲"Daemon Mode"執行,
PyPy實現的GIL一般要比CPython快3x倍。
Jython沒有GIL,由於Jython的線程受益與JVM的內存管理機制。
首先,JavaScript使用的是標記清除算法實現的垃圾回收機制。而CPython須要GIL主要緣由就是內存管理算法。
JavaScript沒有GIL,可是由於它設計的就是單線程,因此它並不須要。JavaScript的event loop和Promise/Callback機制來進行異步編碼實現併發。Python也有相似的asyncio的event-loop機制。
若是你在終端使用python myscript.py
運行,CPython將會進行一系列的讀取,詞法分析,語法分析,編譯,解釋和執行代碼。
一個很是重要點事,在編譯過程當中生成的.pyc文件,Python3是放置在__pycache__
目錄下,Python2是在文件相同目錄下。該文件就是Python裏面的字節碼,在執行文件不會生成,只會在倒入的模塊或者第三方模塊生成。
因此,Python解釋成字節碼而且進行運行。與Java和C#.NET相比:
Java 編譯成一箇中間語言,而後JVM加載字節碼,進行just in time編譯成機器碼。.NET CLI也是一樣的方式,.NET common language runtime使用just in time編譯成機器碼
那麼,Python爲何要比Java和C#測試性能差那麼多,都是使用字節碼,區別就在於JIT編譯方式。
Just in time須要一箇中間語言容許代碼被拆爲多個chunks(或者frames),AOT編譯器設計用來確保CPU可以理解裏面的內容。
JIT自己沒有提升代碼執行,可是由於它執行仍然是字節碼。而後,JIT容許在運行中優化執行。一個好的JIT加應用程序執行很高的代碼,將字節碼直接優化爲機器碼,從而提升執行效率,這種技術成爲 Hot Spot。
也就是說在程序一次次執行過程當中,會變得愈來愈快。還有就是,Java和C#是強類型語言,因此優化器可以進一步優化。
PyPy也有JIT,設計比CPython執行更快。
JIT也有缺點,就是減慢了啓動時間。CPython啓動時間已經很慢了,PyPy更是比CPython慢2-3x倍。JVM啓動速度是臭的不行。.NET CLR在系統啓動的時候就先啓動了。
若是你有一個Python進程要長時間運行,那麼可使用JIT的hot spots帶來的益處。
然而,CPython設計爲通用語言。因此,當你在實現命令行應用時,每次都要長時間等待JIT啓動那是很是討厭的事情。
CPython也作不少嘗試,也嘗試性使用Plugging方式加入JIT,可是項目如今是停滯的。
在靜態類型語言中,你必須在申明時就定義它的類型,這些語言有C,C++,Java,C#和Go。
在一個動態類型元中,雖然也有類型的概念,可是一個變量的類型是動態的
a = 1 a = "foo"
在上面示例中,Python使用了相同的變量賦於了不一樣的str類型,它會釋放第一次建立的內存。
靜態語言並非設計來讓你編碼頭疼,而是爲了適應CPU的操做方式。由於全部的操做都是二進制操做,你須要將全部的對象和類型轉換爲低級別的數據結構。
Python已經爲了作了這件事情,你看不見也不須要關係。
不用申明類型並非致使Python變慢的直接緣由,這樣設計讓你幾乎全部操做都是動態的。你能夠在運行時替換對象的方法,你能夠在運行時對底層系統調用進行monkey-patch操做,一切皆有可能。
這樣的設計致使Python很難進行優化。
爲了驗證個人觀點,我將在個人Ubuntu系統中使用syscall追蹤工具Dtrace。CPython並不內建DTrace,因此你須要從新編譯CPython,我將使用Python 3.7.0:
./configure --with-dtrace make
如今代碼中就可使用Dtrace進行追蹤了,你能夠下載工具來對Python的函數調用,執行時間,CPU時間,syscalls等進行分析:
sudo dtrace -s toolkit/<tracer>.d -c ‘../cpython/python.exe script.py’
py_callflow追蹤器打印了相似以下信息:
因此,Python動態類型是否讓它變慢呢
Python主要慢的緣由是由於它的動態和靈活性。它能夠做爲解決大多數問題的工具,也存在更優更快的可選方案。
能夠在應用程序中利用async,理解性能工具,考慮使用多解釋器等進行優化。
若是對於啓動時間不是那麼關心的話能夠考慮使用JIT,例如PyPy。
對於性能要求比較苛刻的,你可使用更多的靜態類型變量,考慮使用Cython