一文看懂,為什么 Python 運(yùn)行速度如此慢?
在編程社區(qū)中,眾所周知Python編程語(yǔ)言在速度方面并不占優(yōu)勢(shì)。
"但是就是慢..."
在這篇文章中,我將介紹Python的不同特性,我們將了解為什么這使其成為當(dāng)今最完整的語(yǔ)言之一,但速度不夠快。但首先,讓我們掌握一些關(guān)于編程語(yǔ)言的基本知識(shí)。
抽象級(jí)別
正如我們可能知道的那樣,編程語(yǔ)言通常根據(jù)其抽象級(jí)別進(jìn)行描述。
- 低抽象級(jí)別表明該語(yǔ)言更接近硬件(難以解釋)
- 高級(jí)別表示代碼更接近用戶(易于解釋)。
抽象級(jí)別(從硬件到現(xiàn)代編程語(yǔ)言)
C++、PHP、Java、Python等都被認(rèn)為是現(xiàn)代(或高級(jí))編程語(yǔ)言,因?yàn)樗鼈兛梢栽趲缀跞魏晤愋偷南到y(tǒng)上運(yùn)行。在匯編語(yǔ)言中,我們必須根據(jù)每個(gè)特定處理器的指令編寫(xiě)不同的程序(無(wú)法在不同的CPU上運(yùn)行相同的代碼)。例如,如果我們創(chuàng)建一個(gè)打印“Hello world”的程序并將代碼發(fā)送給我們的朋友(他有不同的計(jì)算機(jī)型號(hào)),當(dāng)他嘗試執(zhí)行它時(shí),它可能會(huì)失敗。
現(xiàn)代語(yǔ)言:金字塔的最后抽象層
現(xiàn)代語(yǔ)言抽象
盡管是離機(jī)器碼最高的抽象,但在金字塔的最后一層也有層次結(jié)構(gòu)。一方面,我們可以找到過(guò)程化語(yǔ)言,如C,我們需要逐步知道自己在做什么。這具有非常高效的優(yōu)點(diǎn),但缺點(diǎn)是復(fù)雜且不夠靈活。另一方面,其他語(yǔ)言通過(guò)讓我們使用更易讀和靈活的代碼來(lái)簡(jiǎn)化任務(wù)。這就是Python的情況。我們幾乎可以用它做任何事情,而且易于實(shí)現(xiàn),但在某些任務(wù)上效率不高。
但為什么Python確切地說(shuō)是“慢”呢?
讓我們回顧一些語(yǔ)言特性以回答這個(gè)問(wèn)題。
解釋性語(yǔ)言
首先,Python是一種解釋性語(yǔ)言,這意味著代碼由軟件程序(稱為解釋器)逐行讀取和執(zhí)行,在運(yùn)行時(shí)進(jìn)行。這是將代碼轉(zhuǎn)換為機(jī)器代碼的一種方式。
編譯型語(yǔ)言
另一種使代碼“為機(jī)器可理解”的方式是通過(guò)編譯過(guò)程。在這種情況下,源代碼在實(shí)際在計(jì)算機(jī)上運(yùn)行之前通過(guò)編譯器轉(zhuǎn)換為機(jī)器代碼。
編譯型與解釋型語(yǔ)言
為什么解釋性方式更慢?
在解釋性語(yǔ)言中,源代碼的每一行在執(zhí)行過(guò)程中都會(huì)即時(shí)轉(zhuǎn)換為機(jī)器代碼。這意味著每次程序運(yùn)行時(shí),解釋器必須解析、分析和執(zhí)行代碼,這增加了與直接運(yùn)行預(yù)編譯機(jī)器代碼相比的開(kāi)銷。例如:如果某段代碼運(yùn)行多次(例如,在循環(huán)內(nèi)),解釋器必須每次遇到時(shí)讀取和轉(zhuǎn)換它。相反,編譯程序?qū)⒅苯舆\(yùn)行機(jī)器代碼,無(wú)需在通過(guò)循環(huán)時(shí)重新翻譯它。
CPython及其全局解釋器鎖(GIL)
標(biāo)準(zhǔn)的Python解釋器是CPython。它由C和Python編寫(xiě),將Python代碼編譯成字節(jié)碼,然后進(jìn)行解釋。為了防止多個(gè)本機(jī)線程同時(shí)執(zhí)行Python字節(jié)碼,CPython使用全局解釋器鎖。這個(gè)鎖是必要的,因?yàn)镃Python的內(nèi)存管理不是線程安全的。然而,在多線程程序中,它可能是一個(gè)顯著的瓶頸,限制了在多核處理器上進(jìn)行多線程的性能提升。
全局解釋器鎖的工作流程
動(dòng)態(tài)類型
此外,Python是動(dòng)態(tài)類型的,這意味著在初始化變量時(shí)不需要聲明變量的類型。這對(duì)效率有何影響呢?嗯,在動(dòng)態(tài)類型語(yǔ)言中,類型是在運(yùn)行時(shí)確定的。這意味著解釋器需要在執(zhí)行代碼片段時(shí)進(jìn)行類型檢查。這需要額外的處理來(lái)確定每個(gè)變量的類型以及根據(jù)這些類型執(zhí)行操作的方式。而動(dòng)態(tài)類型語(yǔ)言的對(duì)立面是什么?
靜態(tài)類型語(yǔ)言
在這種情況下,變量的類型在編譯時(shí)而不是在運(yùn)行時(shí)確定。因此,類型在編譯時(shí)已知,編譯器可以更激進(jìn)地優(yōu)化代碼執(zhí)行。這導(dǎo)致更快但不夠靈活的程序。一些采用這種方法的語(yǔ)言包括C++和Rust。
靜態(tài)類型與動(dòng)態(tài)類型語(yǔ)言
垃圾收集
垃圾收集是一種編程語(yǔ)言運(yùn)行時(shí)系統(tǒng)用于回收程序不再使用的內(nèi)存的自動(dòng)內(nèi)存管理形式。Python通過(guò)垃圾收集自動(dòng)管理其對(duì)象的內(nèi)存分配和釋放。它使用的主要垃圾收集方法是引用計(jì)數(shù)。Python中的每個(gè)對(duì)象都有一個(gè)引用計(jì)數(shù),即指向它的引用數(shù)量。當(dāng)引用計(jì)數(shù)降至零,即不再有指向該對(duì)象的引用時(shí),它會(huì)立即從內(nèi)存中刪除。
垃圾收集器的工作流程
然而,垃圾收集是一把雙刃劍...
它通過(guò)自動(dòng)清理未使用的對(duì)象極大簡(jiǎn)化了內(nèi)存管理,有助于防止由于手動(dòng)內(nèi)存管理導(dǎo)致的內(nèi)存泄漏和其他錯(cuò)誤。但它引入了開(kāi)銷和不可預(yù)測(cè)性,可能影響應(yīng)用程序的性能。