打開網(wǎng)易新聞 查看精彩圖片

優(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 本身更快,開箱即用?多年來,答案逐漸變得清晰:“也許可以,但這很難。

打開網(wǎng)易新聞 查看精彩圖片
打開網(wǎng)易新聞 查看精彩圖片

為什么讓 Python 變得更快這么難?

Python 的性能和它是解釋型語言這件事沒有太大關(guān)系。最大的問題在于,Python 本身的“靈活性”——也就是它的動態(tài)性。

像 C++ 或 Rust 這樣的語言,如果你給某個東西命名,編譯器就能根據(jù)這個名稱推斷出你是什么類型的數(shù)據(jù)。編譯器可以利用這種一致性,生成高效的代碼來處理它。

但在 Python 中,這種保證不存在。一個命名的對象可以是任何東西。每次你要用這個對象時,必須去查找它的類型,再決定能做什么。因此,很多常見的優(yōu)化根本做不了。

打開網(wǎng)易新聞 查看精彩圖片

為什么 Python 的類型提示救不了我們

Python 最近添加了類型提示功能,實際上在這里并不管用。它的目的是在代碼運行前進行檢查,而不是為了提高性能。其設(shè)計初衷就是讓開發(fā)者提前檢查代碼的正確性,同時保持 Python 的靈活性。

那么為什么不創(chuàng)建一個使用類型提示來生成優(yōu)化代碼的 Python 方言呢?實際上,Cython 就有點類似。它能讓代碼更快,但只有在處理原生數(shù)據(jù)類型時,才能發(fā)揮較大作用。一旦你開始使用 Python 的對象類型(比如列表或字典),就得回到 CPython 運行時,性能就沒那么好了。

不過,這個方法其實也不符合我們的初衷:我們不應(yīng)該為了加速而離開 Python。所以問題就來了:能不能在 Python 內(nèi)部搞定呢?

打開網(wǎng)易新聞 查看精彩圖片

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ù)。

打開網(wǎng)易新聞 查看精彩圖片

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ù)等。

打開網(wǎng)易新聞 查看精彩圖片

為什么加速 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ā)展。