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