優(yōu)化 Python 并不簡單,因為這門語言本身非常靈活。下面是為什么即使優(yōu)化有難度,它仍然是提升 Python 性能的最佳途徑。
原文鏈接:https://www.infoworld.com/article/3855600/making-python-faster-wont-be-easy-but-itll-be-worth-it.html
作者 | Serdar Yegulalp 翻譯 | 蘇宓
出品 | CSDN(ID:CSDNnews)
想讓一個 Python 用戶很生氣?或許你只要說一句:“Python 很慢?!?/p>
在很多關(guān)鍵方面,的確如此。沒有外部 C 語言編寫的庫的“純”Python,在計算和處理對象時,遠(yuǎn)不如 C、C++、Java、Rust 或者 Go 那樣快……
為了應(yīng)對這個問題,Python 用戶長期以來采用了繞過“純 Python”的方式。想要更快的數(shù)學(xué)計算?用像 NumPy 或 Numba 這樣的庫,或者用 Cython 把代碼編譯成 C。這些外部解決方案確實有效,而且 Python 社區(qū)中有一整套文化專門研究如何高效使用它們。
但這些“捷徑”總是有代價的。用 NumPy 時,你的代碼需要變得更抽象,不像其他語言那樣細(xì)節(jié)豐富。用 Numba(或者某些情況下是 Cython)時,你只能用 Python 語言的一個小子集來表達你要做的事,并且這種方式能達到的速度是有限的。
總的來說,大家一直在呼吁一個問題:我們能不能讓 Python 本身更快,開箱即用?多年來,答案逐漸變得清晰:“也許可以,但這很難。”


為什么讓 Python 變得更快這么難?
Python 的性能和它是解釋型語言這件事沒有太大關(guān)系。最大的問題在于,Python 本身的“靈活性”——也就是它的動態(tài)性。
像 C++ 或 Rust 這樣的語言,如果你給某個東西命名,編譯器就能根據(jù)這個名稱推斷出你是什么類型的數(shù)據(jù)。編譯器可以利用這種一致性,生成高效的代碼來處理它。
但在 Python 中,這種保證不存在。一個命名的對象可以是任何東西。每次你要用這個對象時,必須去查找它的類型,再決定能做什么。因此,很多常見的優(yōu)化根本做不了。

為什么 Python 的類型提示救不了我們
Python 最近添加了類型提示功能,實際上在這里并不管用。它的目的是在代碼運行前進行檢查,而不是為了提高性能。其設(shè)計初衷就是讓開發(fā)者提前檢查代碼的正確性,同時保持 Python 的靈活性。
那么為什么不創(chuàng)建一個使用類型提示來生成優(yōu)化代碼的 Python 方言呢?實際上,Cython 就有點類似。它能讓代碼更快,但只有在處理原生數(shù)據(jù)類型時,才能發(fā)揮較大作用。一旦你開始使用 Python 的對象類型(比如列表或字典),就得回到 CPython 運行時,性能就沒那么好了。
不過,這個方法其實也不符合我們的初衷:我們不應(yīng)該為了加速而離開 Python。所以問題就來了:能不能在 Python 內(nèi)部搞定呢?

Python 現(xiàn)在如何加速?
最近幾個版本的 CPython(Python 的官方實現(xiàn))引入了一些優(yōu)化提議——有些是短期內(nèi)可以實現(xiàn)的,有些則是遠(yuǎn)期目標(biāo)。這些提議是 Python 開發(fā)者打算從內(nèi)部加速 Python 的初步嘗試。
一種新的優(yōu)化方式是專門化自適應(yīng)解釋器(https://docs.python.org/3/whatsnew/3.11.html#whatsnew311-pep659),它試圖利用一些代碼區(qū)域中對象類型的穩(wěn)定性。如果某個操作總是使用同樣的類型,那么可以在運行時把通用字節(jié)碼替換成專門為該類型優(yōu)化過的字節(jié)碼,減少查找的次數(shù)。

Python 的另一個替代實現(xiàn)——PyPy,使用即時編譯(JIT)加速 Python。PyPy 通過生成機器本地代碼來提升性能,但它是一個完全獨立的 Python 實現(xiàn),這就帶來了維護、錯誤、兼容性等一系列問題。CPython 最近也在嘗試引入 JIT 技術(shù),但目前還沒有帶來顯著的性能提升,主要是為將來的改進做準(zhǔn)備。
另一個新變化是沒有全局解釋器鎖(GIL)的 CPython 版本。GIL 是用來同步 Python 中多線程活動的,但這也限制了真正的并行執(zhí)行。沒有 GIL 的版本給多線程性能提升開辟了新天地,雖然它目前仍處于實驗階段。
這些只是正在推進的一些改進,未來還會有更多。我們可以看到,沒有一種技術(shù)能立刻讓 Python 變得飛快。加速 Python 的關(guān)鍵是盡量讓解釋器少做一些事情,比如避免類型檢查,減少引用計數(shù)等。

為什么加速 Python 必須在 Python 內(nèi)部完成
在這些優(yōu)化出現(xiàn)之前,曾有人提出這樣一個想法:與其讓 Python 變得更快,或者開發(fā)一個新的 Python 運行時(比如PyPy),不如開發(fā)一個與 Python 高度兼容的新語言,并逐步將 Python 用戶遷移到這個新語言上。
這就是 等項目的做法。Mojo 有 Python 類似的語法,甚至在一定程度上支持 Python 的代碼兼容性。但與 Python 不同,Mojo 默認(rèn)編譯成機器本地代碼。
不過,這種語言并不能完全替代 Python,在需要回退到 Python 兼容性時,性能優(yōu)勢就會喪失。任何語言,無論是 Mojo 還是其他類似項目,都很難實現(xiàn)與 Python 現(xiàn)有生態(tài)系統(tǒng)的完全兼容,或者獲得 Python 已經(jīng)擁有的廣泛接受度和用戶基礎(chǔ)。
所以,Python 本身應(yīng)該成為最好的替代品。這個目標(biāo)可能只能一步步實現(xiàn),但這種迭代過程讓現(xiàn)有的 Python 社區(qū)能夠與語言一起發(fā)展,而不是從頭開始重新創(chuàng)造。某些改進(比如去掉 GIL 的 Python)會比其他改進(比如類型專門化)更具影響力,但最重要的是創(chuàng)新不斷涌現(xiàn)。
接下來,就看 Python 社區(qū)如何利用這些創(chuàng)新,推動 Python 不斷向前發(fā)展。
熱門跟貼