偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

你知道為什么Python這么慢?

開發(fā) 后端 新聞
Python 現(xiàn)在越來越火,已經(jīng)迅速擴(kuò)張到包括 DevOps、數(shù)據(jù)科學(xué)、Web 開發(fā)、信息安全等各個領(lǐng)域當(dāng)中。然而,相比起 Python 擴(kuò)張的速度,Python 代碼的運(yùn)行速度就顯得有點(diǎn)遜色了。

 [[247722]]

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

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

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

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

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

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

主流的說法有這些:

  • “是全局解釋器鎖Global Interpreter Lock(GIL)的原因”
  • “是因?yàn)?Python 是解釋型語言而不是編譯型語言”
  • “是因?yàn)?Python 是一種動態(tài)類型的語言”

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

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

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

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

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

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

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

這會對 Python 程序的性能有什么影響?

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

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

由 David Beazley 提供的 GIL 競爭情況圖http://dabeaz.blogspot.com/2010/01/python-gil-visualized.html

由 David Beazley 提供的 GIL 競爭情況圖http://dabeaz.blogspot.com/2010/01/python-gil-visualized.html

對于一個 web 應(yīng)用(例如 Django),同時還使用了 WSGI,那么對這個 web 應(yīng)用的每一個請求都運(yùn)行一個單獨(dú)的 Python 解釋器,而且每個請求只有一個鎖。同時因?yàn)?Python 解釋器的啟動比較慢,某些 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)中也有一個類似的 asyncio 事件循環(huán)。

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

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

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

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

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

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

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

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

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

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

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

那為什么 CPython 不使用 JIT 呢?

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

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

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

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

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

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

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

  1. a = 1
  2. a = "foo"

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

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

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

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

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

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

  1. wget https://github.com/python/cpython/archive/v3.6.6.zip
  2. unzip v3.6.6.zip
  3. cd v3.6.6
  4. ./configure --with-dtrace
  5. make

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

  1. sudo dtrace -s toolkit/<tracer>.d -c ‘../cpython/python.exe script.py

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

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

  • 類型比較和類型轉(zhuǎn)換消耗的資源是比較多的,每次讀取、寫入或引用變量時都會檢查變量的類型
  • Python 的動態(tài)程度讓它難以被優(yōu)化,因此很多 Python 的替代品能夠如此快都是為了提升速度而在靈活性方面作出了妥協(xié)
  • Cython 結(jié)合了 C 的靜態(tài)類型和 Python 來優(yōu)化已知類型的代碼,它可以將性能提升 84 倍。

總結(jié)

由于 Python 是一種動態(tài)、多功能的語言,因此運(yùn)行起來會相對緩慢。對于不同的實(shí)際需求,可以使用各種不同的優(yōu)化或替代方案。

例如可以使用異步,引入分析工具或使用多種解釋器來優(yōu)化 Python 程序。

對于不要求啟動時間且代碼可以充分利用 JIT 的程序,可以考慮使用 PyPy。

而對于看重性能并且靜態(tài)類型變量較多的程序,不妨使用 Cython

 

責(zé)任編輯:龐桂玉 來源: Linux中國
相關(guān)推薦

2024-04-03 09:23:31

ES索引分析器

2018-08-16 08:03:21

Python語言解釋器

2023-03-09 08:23:07

序列化?接口方法

2023-09-08 08:35:42

層疊樣式表CSS

2023-06-26 08:20:02

openapi格式注解

2023-11-02 10:22:29

gRPC后端通信

2014-07-15 11:05:30

黑莓

2024-12-02 12:34:06

2020-08-14 09:11:29

RedisQPS數(shù)據(jù)庫

2021-05-29 06:23:47

webpack esbuild

2016-12-28 11:28:19

.NET反射

2020-08-13 09:19:10

Kafka存儲MQ

2022-06-30 08:01:53

mysqlmyisamcount

2019-12-09 10:29:04

Go語言開發(fā)互聯(lián)網(wǎng)

2025-06-20 04:55:00

Wi-Fi組網(wǎng)CSSR

2015-09-09 11:04:28

Wi-Fi網(wǎng)速

2021-10-13 09:00:19

排序數(shù)據(jù)集開發(fā)

2021-03-18 14:34:34

達(dá)達(dá)集團(tuán)京東云電商

2016-03-16 10:43:08

項(xiàng)目時間

2013-06-24 10:05:40

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號