亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

python 運行速度慢的原因

系統 3143 0

作者:Anthony Shaw 是 Python 軟件基金會成員和 Apache 基金會成員。

近來Python可謂人氣驟升。這門編程語言用于開發運維(DevOps)、數據科學、網站開發和安全。

然而,它沒有因速度而贏得任何獎牌。

Java在速度方面與C、C++、C#或Python相比如何?答案很大程度上取決于你運行的應用程序的類型。沒有哪個基準測試程序盡善盡美,不過The Computer Language Benchmarks Game(計算機語言基準測試游戲)是個不錯的起點。

十多年來,我一直提到計算機語言基準測試游戲;與Java、C#、Go、Java和C++等其他語言相比,Python是速度最慢的語言之一。除了Java等解釋語言外,這還包括JIT(C#和Java)以及AOT(C和C++)編譯器。

注意:我說“Python”時,其實指這種語言的參考實現:CPython。我會在本文中提到其他運行時環境。

我想回答這個問題:Python運行完成類似的應用程序比另一種語言慢2倍至10倍時,為什么它這么慢,我們能不能讓它更快些?

下面是幾種常見的說法:

  • “它是GIL(全局解釋器鎖)”
  • “這是由于它是解釋的,而非編譯”
  • “這是由于它是一種動態類型語言”

那么,到底上述哪個原因對性能帶來的影響最大?

“它是GIL”

現代計算機搭載擁有多個內核的CPU,有時搭載多個處理器。為了利用所有這些額外的處理能力,操作系統定義了一種名為線程的低級結構:一個進程(比如Chrome瀏覽器)可能生成多個線程,并擁有針對內部系統的指令。這樣一來,如果某個進程特別耗費CPU資源,該負載可以在諸多核心之間分擔,這實際上讓大多數應用程序更快地完成任務。

我在寫這篇文章時,我的Chrome瀏覽器有44個線程開著。請記住這點:線程的結構和API在基于POSIX的操作系統(比如Mac OS和Linux)與Windows OS之間是不同的。操作系統還處理線程的調度。

如果你之前沒有從事過多線程編程,需要盡快熟悉的一個概念就是鎖(lock)。與單線程進程不同,當你需要確保改變內存中的變量時,多個線程并不同時試圖訪問/改變同樣的內存地址。

CPython創建變量時,它會分配內存,然后計算該變量的引用有多少,這個概念名為引用計數(reference counting)。如果引用數為0,那么它從系統釋放這部分內存。這就是為什么在某個代碼段(比如for循環的范圍)內創建一個“臨時”變量不會搞砸應用程序的內存消耗。

當變量在多個線程內共享時,就出現了這個難題:CPython如何鎖定引用計數。有一個“全局解釋器鎖”,它小心地控制線程執行。解釋器一次只能執行一個操作,無論它有多少線程。

這對Python應用程序的性能來說意味著什么?

如果你有單線程、單個解釋器的應用程序,這對速度不會有影響。刪除GIL根本不會影響你代碼的性能。

如果你想通過使用線程機制在單個解釋器(Python進程)內實現并發功能,而且線程是IO密集型(比如網絡IO或磁盤IO),你會看到GIL爭奪的后果。

上圖來自大衛?比茲利(David Beazley)撰寫的《GIL可視化》文章:http://dabeaz.blogspot.com/2010/01/python-gil-visualized.html

如果你有Web應用程序(比如Django),又在使用WSGI,那么針對Web應用程序的每個請求都是一個單獨的Python解釋器,所以每個請求只有一個鎖。由于Python解釋器啟動緩慢,一些WSGI實現擁有“守護進程模式”,這可以讓一個或多個Python進程為你保持活躍狀態。

其他Python運行時環境怎么樣?

PyPy有一個GIL,它通常比CPython快3倍。

Jython之所以沒有GIL,是由于Jython中的Python線程由Java線程表示,受益于JVM內存管理系統。

Java如何執行此任務?

好吧,首先所有Java引擎都使用標記-清除(mark-and-sweep)垃圾收集機制。如上所述,GIL的主要需求是CPython的內存管理算法。

Java沒有GIL,但它也是單線程的,所以它不需要內存管理算法。Java的事件循環和承諾回調(Promise/Callback)模式是實現異步編程以代替并發的方法。Python與asyncio事件循環有相似之處。

“這是由于它一種解釋語言”

我常聽到這個觀點,但覺得這過于簡化了CPython的實際工作方式。如果你在終端上編寫了python my.py,那么CPython會啟動讀取、分析、解析、編譯、解釋和執行代碼的一長串操作。

如果你對這個過程的機理頗感興趣,我之前寫過一篇文章:《6分鐘內修改Python語言》(https://hackernoon.com/modifying-the-python-language-in-7-minutes-b94b0a99ce14)。

這個過程的一個重要節點是創建.pyc文件;在編譯階段,字節碼序列寫入到Python 3中__pycache__/里面的一個文件或Python 2中的同一個目錄。這不僅適用于你的腳本,還適用于導入的所有代碼,包括第三方模塊。

所以在大部分時間(除非你編寫的是只運行一次的代碼?),Python解釋字節碼,并在本地執行。相比之下Java和C#.NET:

Java編譯成一種“中間語言”,Java虛擬機讀取字節碼,并即時編譯成機器碼。.NET CIL也一樣,.NET公共語言運行時環境(CLR)使用即時編譯,將編譯后代碼編譯成機器碼。

那么,既然都使用虛擬機和某種字節碼,為什么Python在基準測試中比Java和C#都要慢得多呢?首先,.NET和Java是JIT編譯型的。

JIT或即時編譯需要一種中間語言,以便將代碼拆分成塊(或幀)。提前(AOT)編譯器旨在確保CPU在任何交互發生之前能理解每一行代碼。

JIT本身不會使執行變得更快,因為它仍然執行相同的字節碼序列。然而,JIT讓代碼在運行時能夠加以優化。一個好的JIT優化器會看到應用程序的哪些部分在頻繁執行,這些代碼稱之為“熱點代碼”(hot spot)。然后,它會對這些代碼進行優化,其辦法是把它們換成更高效的版本。

這就意味著當你的應用程序一次又一次地執行相同的操作時,運行速度可以顯著加快。另外記住一點:Java和C#是強類型語言,因此優化器可以對代碼做出多得多的假設。

PyPy有JIT,如上所述,其速度比CPython快得多。這篇性能基準測試文章作了更詳細的介紹:《哪個Python版本的速度最快?》(https://hackernoon.com/which-is-the-fastest-version-of-python-2ae7c61a6b2b)。

那么,CPython為什么不使用JIT呢?

JIT存在幾個缺點:缺點之一是啟動時間。CPython的啟動時間已經比較慢了,PyPy的啟動時間比CPython還要慢2倍至3倍。眾所周知,Java虛擬機的啟動速度很慢。.NET CLR通過系統開啟時啟動解決了這個問題,但CLR的開發人員還開發了操作系統,CLR在它上面運行。

如果你有一個Python進程長時間運行,代碼因含有“熱點代碼”而可以優化,那么JIT大有意義。

然而,CPython是一種通用實現。所以,如果你在使用Python開發命令行應用程序,每次調用CLI都得等待JIT啟動會慢得要命。

CPython不得不試圖滿足盡可能多的用例(use case)。之前有人試過將JIT插入到CPython中,但這個項目基本上擱淺了。

如果你想獲得JIT的好處,又有適合它的工作負載,不妨使用PyPy。

“這是由于它是一種動態類型語言”

在“靜態類型”語言中,你在聲明變量時必須指定變量的類型。這樣的語言包括C、C++、Java、C#和Go。

在動態類型語言中,仍然存在類型這個概念,但變量的類型是動態的。

在這個示例中,Python創建了一個有相同名稱、str類型的第二個變量,并釋放為a的第一個實例創建的內存。

靜態類型語言不是為了給你添堵而設計的,它們是兼顧CPU的運行方式設計的。如果一切最終需要等同于簡單的二進制操作,你就得將對象和類型轉換成低級數據結構。

Python為你這么做這項工作,你永遠看不到,也不需要操心。

不必聲明類型不是導致Python速度慢的原因,Python語言的設計使你能夠讓幾乎一切都是動態的。你可以通過猴子補丁(monkey-patch),加入對運行時聲明的值進行低級系統調用的代碼。幾乎一切都有可能。

正是這種設計使得優化Python異常困難。

為了說明我的觀點,我將使用可在Mac OS中使用的一種名為Dtrace的系統調用跟蹤工具。CPython發行版并未內置DTrace,所以你得重新編譯CPython。我使用3.6.6進行演示。

python 運行速度慢的原因_第1張圖片

現在python.exe將在整個代碼中使用Dtrace跟蹤器。保羅?羅斯(Paul Ross)寫了一篇關于Dtrace的雜談(https://github.com/paulross/dtrace-py#the-lightning-talk)。你可以下載Python的DTrace啟動文件(https://github.com/paulross/dtrace-py/tree/master/toolkit)來測量函數調用、執行時間、CPU時間、系統調用和各種好玩的指標。比如

py_callflow跟蹤器顯示你應用程序中的所有函數調用。

python 運行速度慢的原因_第2張圖片

那么,Python的動態類型會讓它變慢嗎?

  • 比較和轉換類型的開銷很大,每次讀取、寫入或引用一個變量,都要檢查類型。
  • 很難優化一種極具動態性的語言。Python的許多替代語言之所以快得多,原因在于它們為了性能在靈活性方面作出了犧牲。
  • Cython 結合了C-Static類型和Python來優化類型已知的代碼,可以將性能提升84倍。

結論

Python之所以速度慢,主要是由于動態性和多功能性。它可用作解決各種問題的工具,Python有更優化、速度更快的幾個替代方案。

然而,有一些方法可以優化你的Python應用程序,比如通過充分利用異步、深入了解分析工具以及考慮使用多個解釋器。

對于啟動時間不重要、代碼會受益于JIT的應用程序來說,不妨考慮PyPy。

對于性能至關重要,又有更多靜態類型變量的部分代碼而言,不妨考慮使用Cython。

本文轉載自:https://465914.kuaizhan.com/36/57/p543294672f5b80


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 蘑菇视频绿巨人小黄鸭 | 亚洲你懂得 | 亚洲欧美久久精品 | 国产片网址 | 性欧美www | 亚洲视频日韩 | 91精品免费观看 | 午夜久久久久久网站 | 欧美另类成人 | 久久在线观看免费视频 | 无遮挡一级毛片视频 | 青青青国产深夜福利视频 | 中文字幕欧美日韩久久 | 久久99热精品免费观看无卡顿 | 手机看片国产福利 | 日本不卡高清免费v日本 | 成人短视频在线观看视频 | 亚洲国产精品67194成人 | 狠狠操大逼 | 久久不卡一区二区三区 | 日韩天堂在线观看 | 国产免费午夜a无码v视频 | 国产精品第二页在线播放 | 99热这里有免费国内精品 | 全免费a级毛片免费看视频免 | 国产成人8x视频一区二区 | 亚洲欧美综合国产不卡 | 在线亚洲激情 | 99国产高清久久久久久网站 | 亚洲水蜜桃久久综合网站 | 欧美在线一区二区三区 | 精品动漫一区二区三区 | 一本清高清dvd日本播放器 | 香蕉网站在线观看 | 狠狠干奇米 | 操视频网站 | 欧美日本高清视频在线观看 | 欧美毛片在线播放观看 | 日韩欧美一中字暮 | 玖玖国产精品 | 久久精品国产亚洲婷婷 |