c#和python同一主机直接udp_为什么Python 如此之慢
原文:
https://hackernoon.com/why-is-python-so-slow-e5074b6fe55b?hackernoon.comPython 正在爆炸般流行起來,它被用于DevOps, 數據處理,web開發和安全領域。但是在速度方面卻沒有取得過什么勝利。
Java在速度方面和C/C++/C#/Python比起來如何?答案很大程度上取決于你所運行的應用。沒有什么跑分是完美的,但是編程語言測評游戲(Computer Language Benchmarks Game)是一個很好的切入點。
十多年來編程語言測評游戲一直對我來說是一個參考,相比于其他語言,比如Java, C#, Go, JavaScript, C++, Python是最慢的語言之一。這些語言中包含了JIT編譯器(c#, java),AOT編譯器(c/c++)和解釋型語言js。
注意: 當我說“Python”, 我說的是Python的參考實現CPython, 其他實現這篇文章也會提到。 這篇文章想要回答的一個問題就是:當Python執行一個相同的程序要比別的語言實現慢2-10倍的時候,為什么會這么慢,難道我們不能讓它跑的更快嗎?
下面說一下本文的主要觀點(當然也是客觀事實): "因為Python 有GIL(Global Interpreter Lock, 全局解釋鎖)" “因為Python 是解釋型語言而不是編譯型” “因為Python 是動態類型語言” 哪一個原因影響最大對速度影響最大呢?
GIL
現代計算機的cpu有多核,有時候有多個處理器。為了充分利用多出來的處理能力,操作系統定義了一種更加低級別的單位:線程,讓一個進程可以啟動多個線程來執行系統指令。 這樣的話如果一個進程的CPU資源非常緊張,負載就會均勻的平攤到各個CPU核心,這種方法能夠很高效地讓大多數任務更快完成。 當我寫這篇文章的時候,我的Chrome瀏覽器開啟了44個線程。要記住的是線程的結構和api在POSIX-based的系統(OSX, Linux)和Windows下面是不一樣的。操作系統同樣會管理線程的調度。 如果你沒有進行過多線程編程,你需要快速熟悉一下"鎖"的概念。不像單線程的進程,多線程的進程中,當你更改內存中的變量,你需要確認多個線程不會同時嘗試訪問或修改同一個內存塊。
當CPython創建變量,它會給變量分配內存空間并且計算變量的引用數,如果引用數為0, Python將會釋放掉這塊內存,這就是為什么for循環表達式里的臨時變量不會讓內存爆炸。也是CPython的垃圾回收機制。
接下來的挑戰是,當變量被多個線程共享時,如何鎖住引用數。Python程序執行過程中有一個全局解釋鎖GIL 小心地控制著線程的執行。Python解釋器在同一時間只能執行一個操作,不論有多少個線程。
這對于Python應用的性能表現意味著什么呢?
如果你的應用是單線程單解釋器的,這對你的應用性能毫無影響。移除GIL也不會對你的應用性能有任何影響。 如果你想在同一個解釋器中使用線程進行并發操作,并且你的線程是IO密集型的,那你就會看到GIL的資源爭奪。
David Beazley的博客中對多線程程序中 GIL 的作用進行了可視化:http://dabeaz.blogspot.com/2010/01/python-gil-visualized.html
下圖表示了Python多線程程序中GIL的分配情況。
如果一個Python程序里有多個線程,一個程序運行的時候會拿著GIL,當遇到I/O的時候會放開GIL,但是CPU-bound的線程通常不會進行I/O。Python切換線程的一種作法是每100 ticks檢查一下,可以通過sys.setcheckinterval()修改這個數值。 綜上所述,因為Python線程不能有效利用多核,但是增加了CPU context switch的消耗,所以對于CPU-bound的程序表現很差。更糟糕的是,在多核情況下可能表現會更差,因為系統支持多線程運行但是GIL保證只有一個線程運行,這時候多線程會反復的檢查GIL是否被釋放,但是拿不到GIL(因為有太多線程競爭),有可能導致系統發生Trashing現象。
如果你使用一個web應用(Django)并且使用WSGI,每個請求會有一個單獨的Python解釋器,由于Python的全局解釋鎖啟動很慢,所以有的WSGI實現會有一個“守護模式”,就是先把Python進程啟動起來放著,等待請求進來使用。
那其他的Python實現呢?
PyPy也有GIL但是比CPython快超過三倍。 JPython 沒有GIL, 因為JPython的線程代表一個Java線程,得益于JVM的內存管理機制,JPython不使用GIL。
JavaScript是怎么做的呢
首先所有的Javascript 引擎使用標記-清除的垃圾回收機制。 上面提到過, GIL的需求主要是因為CPython的內存管理算法。 JavaScript 沒有GIL, 但它同時也是單線程的所以它并不需要GIL。JavaScript使用事件循環和Promise/Callback實現異步的編程而不是使用并發。Python也有類似實現asyncio
“因為Python是解釋型語言”
我經常聽說這個言論,我覺得這是一個對于CPython執行方式的粗暴簡化。 如果你在終端執行一個命令(比如python myscript.py),CPython會開始順序執行一大串任務: 讀取,詞法分析,解析,編譯,解釋,執行代碼。
一個重點是.pyc文件的創建。在編譯階段,字節碼串被寫到pycache/下(3.x)或者和py文件相同的文件夾(2.x)。這個操作不僅對你自己的代碼有效,也包括所有你導入的模塊。
所以大多數情況下,Python在本地解釋和執行字節碼,與之相比 Java 和 C#.NET:
Java 編譯成一個“中間語言”,然后JVM讀字節碼實時將其轉化為機器碼,.NET的CIL也一樣,.NET CLR(Common-Language-Runtime, 通用語言運行時),使用的是實時編譯到機器碼(JIT)。所以,如果它們都用到了虛擬機和部分字節碼,為什么Python在跑分上比Java/C#慢這么多呢?
Java/C# 是即時編譯(JIT)的。
JIT要求一個中間語言,以便讓代碼轉換成區塊。AOT編譯是為了可以在交互前保證CPU能理解每一行代碼。
JIT本身不會讓執行速度更快,以為它仍然是在執行相同的字節碼, 然而JIT可以讓實時優化成為可能,一個好的JIT編譯器應用的那一部分被執行很多次,這些部分被稱為“熱點”。 這樣編譯器會將這些部分替換成更加高效的版本。這意味著如果你的程序重復做相同的事情,使用JIT就能顯著提高速度。同時Java/C#是強類型的語言所以優化器可以對語言作出更多預設。
PyPy 使用JIT,前面提到,它比CPython快得多。
所以為什么CPython不用JIT呢
JIT有很多缺點,其中一個就是啟動慢。
CPython 啟動已經相對很慢了, PyPy比CPython啟動慢CPython2-3倍。JVM啟動是出了名的慢。.NET CLR使用隨操作系統啟動來解決這個問題, 但CLR的開發者同時也是其依賴的系統的開發者(win)。
如果你的Python是單進程,運行時間很長,并且有很多重復操作可以被優化,那么使用JIT就很有意義。 但是CPython是通用實現,當使用Python開發命令行工具,每次都等待JIT啟動是非常糟糕的體驗。 CPython需要服務于盡可能廣泛的場景,有可能使用JIT反而會大幅度拖累系統性能。 如果你需要使用JIT并且有一個適合的工作場景,那可以使用PyPy。
“因為Python是動態類型語言”
在靜態類型語言中,聲明變量之前需要聲明變量類型,包括 C, C++, Java, C#, Go。動態類型語言中仍然有類型的概念,但是變量的類型可變。
a這個小例子中, Python 使用相同的變量名創建了類型不同的第二個變量,釋放掉了第一個變量的內存空間。
靜態類型語言并不是為了麻煩你而設計的,而是根據CPU行為設計的。如果所有的行為最終會變成二進制操作,你必須將對象轉換成更加底層的數據結構。
Python為你代勞了轉換到底層數據結構這一步,所以你不用關心。當然不用聲明變量并不是Python慢的原因。 Python語言的設計極為靈活,幾乎所有東西都能變成動態的,比如猴子補丁。這種設計讓Python的優化變得難以想象的困難。
猴子補丁: 1. 在運行時替換方法、屬性等 2. 在不修改第三方代碼的情況下增加原來不支持的功能 3. 在運行時為內存中的對象增加patch而不是在磁盤的源代碼中增加python中一個很簡單的例子:
import所以是Python的動態性讓它如此慢嗎?
比較和轉換類型非常耗費資源, 每次對變量讀寫、引用都需要檢查類型。如此動態化的語言是很難優化的,很多Python的不同實現能更快是因為為了性能在靈活性方面做了妥協。
比如CPython, 將C語言的靜態類型和Python結合,這些靜態類型能提供84倍的性能優化。
結論
Python這么慢主要是因為動態的生態和它的多功能性。它能用來解決所有類型的問題,所以在不同領域我們可以選擇更加快速的Python版本。
有很多方法可以用來優化你的Python程序,比如活用async, 了解分析工具, 考慮使用多個解釋器等等。比如一些啟動時間不重要的應用,或者能夠能JIT獲益的程序我們可以使用PyPy。如果你的代碼有些部分非常要求性能,又使用了很多C語言的靜態類型,那么選擇Cython。
總結
以上是生活随笔為你收集整理的c#和python同一主机直接udp_为什么Python 如此之慢的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Google Chrome谷歌旧版本下载
- 下一篇: c# char unsigned_dll