陜西網(wǎng)站建設(shè)通報(bào)網(wǎng)址搜索
解釋性
python是動(dòng)態(tài)類型解釋性語言,不管使用哪種解釋器
因?yàn)椤敖忉屝哉Z言”這個(gè)概念更多地是指代碼的執(zhí)行方式,而不是編譯方式。在解釋性語言中,代碼在執(zhí)行時(shí)會(huì)一行一行地解釋并執(zhí)行,而不是預(yù)先編譯為機(jī)器語言。而即使使用了PyPy解釋器,PyPy使用了JIT(即時(shí)編譯)技術(shù),它會(huì)在代碼運(yùn)行時(shí)將代碼編譯為機(jī)器語言。其中的JIT編譯技術(shù)仍然在運(yùn)行時(shí)進(jìn)行,代碼仍然在運(yùn)行時(shí)動(dòng)態(tài)確定類型和解釋執(zhí)行。因此,盡管PyPy使用了編譯技術(shù)加速代碼的執(zhí)行,但它仍然是解釋性語言
動(dòng)態(tài)性
動(dòng)態(tài)語言(python)的運(yùn)行速度為什么比靜態(tài)語言(java)慢?
在 Java 和 Python (cpython解釋器)中,都存在先編譯為字節(jié)碼,然后再在解釋器或虛擬機(jī)中將字節(jié)碼轉(zhuǎn)換為機(jī)器語言的過程。那么為什么 Java 的運(yùn)行速度會(huì)比 Python 快,python在運(yùn)行時(shí)動(dòng)態(tài)確定類型和進(jìn)行解釋會(huì)比java慢
靜態(tài)類型的語言比如 C,Java,Go,需要在聲明變量的時(shí)候帶上類型。而 Python 就不用,Python 幫你決定一個(gè)變量是什么類型,并且可以隨意改變。
動(dòng)態(tài)類型為什么慢呢?每次檢查類型和改變類型開銷太大;如此動(dòng)態(tài)的類型,難以優(yōu)化
Python 的動(dòng)態(tài)類型和動(dòng)態(tài)內(nèi)存分配也會(huì)對運(yùn)行速度產(chǎn)生影響。Python 中的對象都是動(dòng)態(tài)創(chuàng)建的,這就需要在運(yùn)行時(shí)進(jìn)行內(nèi)存分配和回收,這會(huì)比靜態(tài)分配更慢。
pypy解釋器的大致運(yùn)行過程
PyPy解釋器既是解釋性的,也是編譯性的。
在PyPy解釋器中,源代碼首先會(huì)被編譯成抽象語法樹(AST),然后被編譯成LLVM IR(Intermediate Representation,中間表示)。接著,LLVM IR會(huì)被傳遞給JIT編譯器,生成對應(yīng)的機(jī)器碼,這些機(jī)器碼被緩存起來以備下次調(diào)用(注意這里是在pypy解釋器中的過程,而只有在運(yùn)行代碼的時(shí)候才會(huì)去用pypy解釋器去解釋)。這個(gè)過程中,JIT編譯器根據(jù)實(shí)際情況來選擇是否對代碼進(jìn)行即時(shí)編譯。如果發(fā)現(xiàn)某些代碼被重復(fù)執(zhí)行(如循環(huán)),JIT編譯器會(huì)對這些代碼進(jìn)行優(yōu)化并將它們編譯成機(jī)器碼,以提高程序性能。如果當(dāng) JIT 編譯器無法對代碼進(jìn)行優(yōu)化時(shí),PyPy 仍然會(huì)使用解釋器來執(zhí)行 Python 代碼。
因此,PyPy解釋器既包含解釋器的特點(diǎn),也包含編譯器的特點(diǎn)。在運(yùn)行過程中,PyPy解釋器會(huì)將源代碼解釋執(zhí)行,同時(shí)也會(huì)將部分代碼編譯成機(jī)器碼,以提高程序性能。因此,可以說PyPy是一種混合型的解釋器/編譯器。
GIL
首先GIL鎖是python 默認(rèn)的cpython解釋器帶的,在此解釋器下創(chuàng)建多線程是用戶級線程,且cpython的多線程模型是多對一模型,即使你創(chuàng)建再多的多線程,也只會(huì)被映射到一個(gè)內(nèi)核級線程上,內(nèi)核級線程去排對競爭cpu。
那為什么cpython的多線程模型是多對一模型呢?
因?yàn)榧词故嵌鄬Χ嗄P?#xff0c;python代碼創(chuàng)建多個(gè)線程(用戶級線程)最終被映射到了多個(gè)內(nèi)核級線程上,然后這些內(nèi)核級線程去排隊(duì)競爭cpu,假設(shè)同時(shí)競爭到了cpu時(shí)間片,但是也只有一個(gè)內(nèi)核線程有GIL鎖,才能調(diào)用cpython解釋器將python字節(jié)碼解釋成機(jī)器碼執(zhí)行,而那些沒有GIL鎖的內(nèi)核線程即使分配到了cpu時(shí)間片,會(huì)發(fā)現(xiàn)沒有GIL鎖,無法調(diào)用cpython解釋器,因此也就無法執(zhí)行字節(jié)碼,從而又被系統(tǒng)放入到阻塞隊(duì)列中去等待GIL鎖的資源。
即同一時(shí)刻只能有一個(gè)內(nèi)核線程獲取GIL鎖然后被解釋器解釋執(zhí)行,那么其它的內(nèi)核線程就會(huì)增加操作系統(tǒng)調(diào)度和上下文切換的開銷,而沒有實(shí)際的性能提升。
那為什么cpython中為什么要有GIL鎖的存在
GIL鎖的存在是為了保證解釋器的線程安全性。因?yàn)镃Python的解釋器內(nèi)部實(shí)現(xiàn)使用了大量的全局變量和共享數(shù)據(jù)結(jié)構(gòu),如果沒有GIL鎖的保護(hù),多個(gè)線程同時(shí)訪問這些數(shù)據(jù)結(jié)構(gòu)會(huì)導(dǎo)致解釋器的崩潰或者產(chǎn)生未定義的行為。通過GIL鎖的機(jī)制,CPython確保了在任意時(shí)刻只有一個(gè)線程可以執(zhí)行解釋器的字節(jié)碼,從而保證了解釋器的線程安全性。
垃圾回收機(jī)制
python的垃圾回收機(jī)制主要是以引用計(jì)數(shù)為主,標(biāo)記-清除是為了解決引用計(jì)數(shù)遺留的循環(huán)引用的問題;分代回收是用空間換時(shí)間的提升回收的效率
1.內(nèi)存占用:由于垃圾回收機(jī)制需要維護(hù)內(nèi)存中的所有對象(每個(gè)對象需要分配單獨(dú)的空間來統(tǒng)計(jì)引用計(jì)數(shù),這無形中加大的空間的負(fù)擔(dān),并且需要對引用計(jì)數(shù)進(jìn)行維護(hù))
2.CPU利用率:在垃圾回收期間,Python解釋器需要進(jìn)行大量的計(jì)算和操作,這會(huì)導(dǎo)致CPU利用率上升,從而降低程序的運(yùn)行速度。
3.垃圾收集時(shí)間:Python解釋器的垃圾回收機(jī)制需要花費(fèi)一定的時(shí)間來掃描內(nèi)存并處理垃圾對象,這可能會(huì)導(dǎo)致Python程序的運(yùn)行速度變慢。
4.阻塞:垃圾回收機(jī)制的階段,會(huì)暫停整個(gè)應(yīng)用程序,等待標(biāo)記清除結(jié)束后才會(huì)恢復(fù)應(yīng)用程序的運(yùn)行。