自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

為什么Python這么慢?

開發(fā) 后端
本篇文章我想要回答這樣一個(gè)問(wèn)題:當(dāng)運(yùn)行同一個(gè)程序時(shí),為什么Python會(huì) 比其他語(yǔ)言慢2到10倍?為什么我們無(wú)法將它變得更快?

[[240271]]

大數(shù)據(jù)文摘出品

編譯:jojo、Hope、張秋玥、CoolBoy

Python語(yǔ)言近年來(lái)人氣爆棚。它廣泛應(yīng)用于網(wǎng)絡(luò)開發(fā)運(yùn)營(yíng),數(shù)據(jù)科學(xué),網(wǎng)絡(luò)開發(fā),以及網(wǎng)絡(luò)安全問(wèn)題中。

然而,Python在速度上完全沒(méi)有優(yōu)勢(shì)可言。

在速度上,Java如何同C,C++,C#或者Python相比較?答案幾乎完全取決于要運(yùn)行的應(yīng)用。在這個(gè)問(wèn)題上,沒(méi)有***的評(píng)判標(biāo)準(zhǔn),然而The Computer Language Benchmarks Game 是一個(gè)不錯(cuò)的方法。

鏈接:http://benchmarksgame.alioth.debian.org

基于我對(duì)The Computer Language Benchmarks Game超過(guò)十年的觀察,相比于Java,C#,Go,JavaScript, C++等,Python是最慢的語(yǔ)言之一。其中包括了 JIT (C#, Java) 和 AOT (C, C++)編譯器,以及解釋型語(yǔ)言,例如JavaScript。

  • 動(dòng)態(tài)編譯:https://en.wikipedia.org/wiki/Just-in-time_compilation
  • 靜態(tài)編譯:https://en.wikipedia.org/wiki/Ahead-of-time_compilation

注意:當(dāng)我提到“Python”時(shí),我指的是CPython這個(gè)官方的解釋器。我也將在本文中提及其他的解釋器。

我想要回答這樣一個(gè)問(wèn)題:當(dāng)運(yùn)行同一個(gè)程序時(shí),為什么Python會(huì) 比其他語(yǔ)言慢2到10倍?為什么我們無(wú)法將它變得更快?

以下是最主要的原因:

  • “它是GIL(Global Interpreter Lock全局解釋器鎖)”
  • “它是解釋型語(yǔ)言而非編譯語(yǔ)言”
  • “它是動(dòng)態(tài)類型語(yǔ)言”

那么以上哪種原因?qū)π阅苡绊?**呢?

“它是全局解釋器鎖”

現(xiàn)代計(jì)算機(jī)的CPU通常是多核的,并且有些擁有多個(gè)處理器。為了充分利用多余的處理能力,操作系統(tǒng)定義了一種低級(jí)的結(jié)構(gòu)叫做線程:一個(gè)進(jìn)程(例如Chrome瀏覽器)可以產(chǎn)生多個(gè)線程并且指導(dǎo)內(nèi)部系統(tǒng)。

如果一個(gè)進(jìn)程是CPU密集型,那么其負(fù)載可以被多核同時(shí)處理,從而有效提高大多數(shù)應(yīng)用的速度。

當(dāng)我寫這篇文章時(shí),我的Chrome瀏覽器同時(shí)擁有44個(gè)線程。注意,基于POSIX(比如MacOS和Linux)和Windows操作系統(tǒng)相比,線程的結(jié)構(gòu)和API是不同的。操作系統(tǒng)也會(huì)處理線程的調(diào)度問(wèn)題。

如果你之前沒(méi)有做過(guò)多線程編程,你需要快速熟悉鎖的概念。區(qū)別于單線程進(jìn)程,你需要確保當(dāng)內(nèi)存中的變量被修改時(shí),多線程不會(huì)同時(shí)試圖訪問(wèn)或者改變同一個(gè)存儲(chǔ)地址。

當(dāng)CPython創(chuàng)建變量時(shí),它會(huì)預(yù)先分配存儲(chǔ)空間,然后計(jì)算當(dāng)前變量的引用數(shù)目。這個(gè)概念被稱為引用計(jì)數(shù)。如果引用計(jì)數(shù)為零,那么它將從系統(tǒng)中釋放對(duì)應(yīng)存儲(chǔ)區(qū)域。

這就是為什么在CPython中創(chuàng)造“臨時(shí)”變量不會(huì)使應(yīng)用占用大量的存儲(chǔ)空間——尤其是當(dāng)應(yīng)用中使用了for循環(huán)這一類可能大量創(chuàng)建“臨時(shí)”變量的結(jié)構(gòu)時(shí)。

當(dāng)存在多個(gè)線程調(diào)用變量時(shí),CPython如何鎖住引用計(jì)數(shù)成為了一個(gè)挑戰(zhàn)。而“全局解釋鎖”應(yīng)運(yùn)而生,它能夠謹(jǐn)慎控制線程的執(zhí)行。無(wú)論有多少的線程,解釋器每次只能執(zhí)行一個(gè)操作。

這對(duì)Python的性能意味著什么呢?

如果你的應(yīng)用基于單線程、單解釋器,那么討論速度這一點(diǎn)就毫無(wú)意義,因?yàn)槿サ鬐IL并不會(huì)影響代碼性能。

如果你想使用線程在單解釋器(Python 進(jìn)程)中實(shí)現(xiàn)并發(fā),并且你的線程為IO密集型(例如網(wǎng)絡(luò)IO或磁盤IO),你就會(huì)看到GIL爭(zhēng)用的結(jié)果。

該圖來(lái)自David Beazley的GIL可視化

如果你有一個(gè)網(wǎng)絡(luò)應(yīng)用(例如Django)并且使用WSGI,那么每一個(gè)對(duì)于你的網(wǎng)絡(luò)應(yīng)用的請(qǐng)求將是一個(gè)獨(dú)立的Python解釋器,因此每個(gè)請(qǐng)求只有一個(gè)鎖。因?yàn)镻ython解釋器啟動(dòng)很慢,一些WSGI便集成了能夠使保持Python進(jìn)程的“守護(hù)進(jìn)程” 。

那么其他Python解釋器的速度又如何呢?

PyPy擁有GIL,通常比CPython快至少三倍。

Jython沒(méi)有GIL,因?yàn)樵贘ython中Python線程是用Java線程表示的,這得益于JVM內(nèi)存管理系統(tǒng)。

JavaScript是如何做到這一點(diǎn)的呢?

  • 首先,所有的Javascript引擎使用標(biāo)記加清除的垃圾收集系統(tǒng),而之前提到GIL的基本訴求是CPython的存儲(chǔ)管理算法。
  • JavaScript沒(méi)有GIL,但因?yàn)樗菃尉€程的,所以也并不需要GIL。
  • JavaScript通過(guò)事件循環(huán)和承諾/回調(diào)模式來(lái)實(shí)現(xiàn)異步編程的并發(fā)。Python有與異步事件循環(huán)相似的過(guò)程。

“因?yàn)樗墙忉屝驼Z(yǔ)言”

我經(jīng)常聽到這句話。我覺(jué)得這只是對(duì)于CPython實(shí)際運(yùn)行方式的一種簡(jiǎn)單解釋。如果你在終端中輸入python myscript.py,那么CPython將對(duì)這段代碼開始一系列的讀取,詞法分析,解析,編譯,解釋和運(yùn)行。

這個(gè)過(guò)程中的重要步驟是在編譯階段創(chuàng)建一個(gè).pyc 文件,這個(gè)字節(jié)碼序列將被寫入Python3下__pycache__/ 路徑中的一個(gè)文件(對(duì)于Python2,文件路徑相同)。這個(gè)步驟不僅僅應(yīng)用于腳本文件,也應(yīng)用于所有導(dǎo)入的代碼,包括第三方模塊。

所以大多時(shí)候(除非你寫的代碼只運(yùn)行一次),Python是在解釋字節(jié)碼并且本地執(zhí)行。下面我們將Java和C#.NET相比較:

Java編譯成一門“中間語(yǔ)言”,然后Java虛擬機(jī)讀取字節(jié)代碼并即時(shí)編譯為機(jī)器代碼。.NET的通用中間語(yǔ)言(CIL)是一樣的,它的通用語(yǔ)言運(yùn)行時(shí)間(CLR)也采用即時(shí)編譯的方法轉(zhuǎn)化為機(jī)器代碼。

那么,如果Python用的是和Java和C#一樣的虛擬機(jī)和某種字節(jié)代碼,為什么在基準(zhǔn)測(cè)試中它卻慢得多?首先,.NET和Java是采用JIT編譯的。

JIT,又稱即時(shí)編譯,需要一種中間語(yǔ)言來(lái)把代碼進(jìn)行分塊(或者叫數(shù)據(jù)幀)。預(yù)編譯(AOT, Ahead of Time)器的設(shè)計(jì)保證了CPU能夠在交互之前理解代碼中的每一行。

JIT本身不會(huì)使執(zhí)行速度更快,因?yàn)樗匀粓?zhí)行相同的字節(jié)碼序列。但是,JIT允許在運(yùn)行時(shí)進(jìn)行優(yōu)化。好的JIT優(yōu)化器可以檢測(cè)哪些部分執(zhí)行次數(shù)比較多,這些部分被稱為“熱點(diǎn)”。然后,它將用更高效的代碼替換它們,完成優(yōu)化。

這就意味著當(dāng)計(jì)算機(jī)應(yīng)用程序需要重復(fù)做一件事情的時(shí)候,它就會(huì)更加地快。另外,我們要知道Java和C#是強(qiáng)類型語(yǔ)言(變量需要預(yù)定義),因此優(yōu)化器可以對(duì)代碼做更多的假設(shè)。

PyPy使用即時(shí)編譯器,并且前文也有提到它比CPython更快。這篇關(guān)于基準(zhǔn)測(cè)試的文章介紹得更為詳細(xì)——什么版本的Python最快?

鏈接:

https://hackernoon.com/which-is-the-fastest-version-of-python-2ae7c61a6b2b

那么,為什么CPython不使用即時(shí)編譯器呢?

JIT存在一些缺點(diǎn):其中一個(gè)是啟動(dòng)時(shí)間。CPython啟動(dòng)時(shí)間已經(jīng)相對(duì)較慢,PyPy比CPython還要慢2-3倍。眾所周知,Java虛擬機(jī)的啟動(dòng)速度很慢。為了解決這個(gè)問(wèn)題,.NET CLR在系統(tǒng)啟動(dòng)的時(shí)候就開始運(yùn)行,但CLR的開發(fā)人員還開發(fā)了專門運(yùn)行CLR的操作系統(tǒng)來(lái)加快它。

如果你有一個(gè)運(yùn)行時(shí)間很長(zhǎng)的Python進(jìn)程,并且其代碼可以被優(yōu)化(因?yàn)樗拔乃龅?ldquo;熱點(diǎn)”),那么JIT就能夠起到很大作用。

但是,CPython適用于各類應(yīng)用。因此,如果你使用Python開發(fā)命令行應(yīng)用程序,每次調(diào)用CLI時(shí)都必須等待JIT啟動(dòng),這將非常緩慢。

CPython必須盡量多地嘗試不同的案例以保證通用性,而把JIT插入到CPython中可能會(huì)讓這個(gè)項(xiàng)目停滯不前。

如果你想要借助JIT的力量,而且你的工作量還比較大,那么使用PyPy吧。

“因?yàn)樗且粋€(gè)動(dòng)態(tài)類型語(yǔ)言”

在靜態(tài)類型語(yǔ)言中,定義變量時(shí)必須聲明類型。C, C++, Java, C#, Go都是這種語(yǔ)言。

在動(dòng)態(tài)類型語(yǔ)言中,類型的概念依舊存在,但是這個(gè)變量的類型是動(dòng)態(tài)變化的。

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

在上面這個(gè)例子中,Python創(chuàng)建第二個(gè)變量的時(shí)候用了同樣的名字,但是變量類型是str(字符型),這樣就對(duì)先前在內(nèi)存中給a分配的空間進(jìn)行了釋放和再分配。

靜態(tài)類型語(yǔ)言的這種設(shè)計(jì)并不是為了麻煩大家——它們是按照CPU的運(yùn)行方式設(shè)計(jì)的。如果最終需要將所有內(nèi)容都轉(zhuǎn)化為簡(jiǎn)單的二進(jìn)制操作,那就必須將對(duì)象和類型轉(zhuǎn)換為低級(jí)數(shù)據(jù)結(jié)構(gòu)。

Python自動(dòng)完成了這個(gè)過(guò)程,我們看不見(jiàn),也沒(méi)必要看見(jiàn)。

不必聲明類型不是使Python變慢的原因。Python語(yǔ)言的設(shè)計(jì)使我們幾乎可以創(chuàng)建任何動(dòng)態(tài)變量。我們可以在運(yùn)行時(shí)替換對(duì)象中的方法,也可以胡亂地把低級(jí)系統(tǒng)調(diào)用賦給一個(gè)值。幾乎怎么修改都可以。

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

為了闡明我的觀點(diǎn),我將使用一個(gè)MacOS中的應(yīng)用。它是一個(gè)名為Dtrace的系統(tǒng)調(diào)用跟蹤工具。CPython發(fā)行版沒(méi)有內(nèi)置DTrace,因此你必須重新編譯CPython。以下演示中使用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 

現(xiàn)在python.exe將在整條代碼中使用Dtrace跟蹤器。Paul Ross就Dtrace做了一篇很棒的短演講。 你可以下載Python的DTrace啟動(dòng)文件來(lái)測(cè)試函數(shù)調(diào)用、執(zhí)行時(shí)間、CPU時(shí)間、系統(tǒng)調(diào)用等各種有意思的事情。例如:

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

DTrace啟動(dòng)文件:https://github.com/paulross/dtrace-py/tree/master/toolkit

演講鏈接:https://github.com/paulross/dtrace-py#the-lightning-talk

py_callflow跟蹤器顯示應(yīng)用程序中的所有函數(shù)調(diào)用:

因此,是Python的動(dòng)態(tài)類型讓它變慢的嗎?

  • 比較和轉(zhuǎn)換類型是耗時(shí)的,因?yàn)槊看巫x取、寫入變量或引用變量類型時(shí)都會(huì)進(jìn)行檢查
  • 很難優(yōu)化一種如此動(dòng)態(tài)的語(yǔ)言。其他語(yǔ)言之所以那么快是因?yàn)樗麄儬奚艘欢ǖ撵`活性,從而提高了性能。
  • 了解一下Cython,它結(jié)合了C-Static類型和Python來(lái)優(yōu)化已知類型的代碼,可以提供84倍速度的性能提升。

結(jié)論

Python的緩慢主要是由于它動(dòng)態(tài)和多用途的特點(diǎn)。它可以用于解決幾乎所有問(wèn)題,但是更加優(yōu)化而快捷的替代方案可能存在。

但是,有一些方法可以通過(guò)利用異步計(jì)算,理解分析工具,以及考慮使用多個(gè)解釋器來(lái)優(yōu)化Python應(yīng)用程序。

對(duì)于有些啟動(dòng)時(shí)間相對(duì)不重要,并且即時(shí)編譯器(JIT)可以提高效率的應(yīng)用,可以考慮使用PyPy。

對(duì)于性能優(yōu)先并且有更多靜態(tài)變量的代碼部分,請(qǐng)考慮使用Cython。

相關(guān)報(bào)道:https://hackernoon.com/why-is-python-so-slow-e5074b6fe55b

【本文是51CTO專欄機(jī)構(gòu)大數(shù)據(jù)文摘的原創(chuàng)譯文,微信公眾號(hào)“大數(shù)據(jù)文摘( id: BigDataDigest)”】

     大數(shù)據(jù)文摘二維碼

戳這里,看該作者更多好文

責(zé)任編輯:趙寧寧 來(lái)源: 51CTO專欄
相關(guān)推薦

2018-10-28 15:40:23

Python編程語(yǔ)言

2021-05-29 06:23:47

webpack esbuild

2016-12-28 11:28:19

.NET反射

2020-08-14 09:11:29

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

2022-06-30 08:01:53

mysqlmyisamcount

2015-09-09 11:04:28

Wi-Fi網(wǎng)速

2020-05-25 20:46:59

Python編程語(yǔ)言程序員

2020-02-27 15:44:41

Nginx服務(wù)器反向代理

2013-03-04 10:10:36

WebKit瀏覽器

2019-08-30 14:58:47

JavaScript程序員編程語(yǔ)言

2024-02-26 21:15:20

Kafka緩存參數(shù)

2022-06-02 08:03:19

PyCharmPython代碼

2020-02-27 21:03:30

調(diào)度器架構(gòu)效率

2022-06-13 21:52:02

CDN網(wǎng)絡(luò)節(jié)點(diǎn)

2025-03-04 07:30:00

開發(fā)前端Node.js

2012-08-17 10:01:07

云計(jì)算

2020-03-30 15:05:46

Kafka消息數(shù)據(jù)

2020-06-16 14:13:50

Kubernetes容器Linux

2014-05-26 17:00:51

2022-05-30 10:23:59

HTTPHTTP 1.1TCP
點(diǎn)贊
收藏

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