如果用 C++20 來寫 Python 的 Implementation,會有多少性能提升?
CPython 是 Python 的 C Implementation, C++20 有很多類 Python 的寫法,如果有 Python 的 C++ Based Implementation,是否可能比 CPython 更快?
C++ 20 長得再像 Python 也和用 C++ 20 去實現一個 Python 解釋器能有多少性能提升沒有半毛錢關係
我日常不寫 Python,但對腳本語言 Runtime 的性能做過些調研。這裡拋磚引玉下簡單的結論:關鍵根本不在於用什麼語言實現,而在於是否用上 JIT。
我們不妨將題主提到的 CPython 作為基準,把它運行計算密集型任務的性能水平設為 1。那麼在此基礎上大概可以得到:
- 在 1 這個量級,有經典的 Lua 和 Ruby 解釋器,以及最近的 QuickJS 和 Hermes JS 引擎。
- 在約 20 到 50 這個量級,有 LuaJIT 和 V8 這種帶 JIT 的引擎。
- 在約 200 以上的量級,有 Linux 和 Windows 這種操作系統(原生語言的 Runtime)。
我不太確定 WASM 和 Java 這種加了層 VM 的「原生」性能水平如何?粗略猜測在 100 左右,望不吝賜教。
然後讓我們看看,這幾個性能量級的 Runtime 是怎麼實現的:
- 最慢、最簡單的解釋器是什麼語言寫的?既有 C 也有 C++
- 一言不合快個幾十倍的 JIT 引擎是什麼語言寫的?既有 C 也有 C++
- 最硬核的操作系統是什麼語言寫的?還是那句話……既有 C 也有 C++
所以,用 C 還是 C++ 寫 Implementation 重要嗎?不重要。比挑選編程語言更重要的,是基本的工程設計,以及實際場景下的取捨。你可能會有疑問,這性能都差了幾十倍,還有什麼取捨的必要嗎?這裡個人的理解是這樣的:
- 最經典的解釋器,原理不外乎逐行解釋執行代碼。它自身速度雖慢,但非常輕便靈活。這種解釋器通常在幾萬行代碼內就可以實現,編譯到二進位的體積多在 1M 以下,非常容易嵌入到大型原生項目(如遊戲引擎)中,易於在低配硬體上運行,也非常容易與原生模塊互調。
- JIT 雖然能比解釋器快幾十倍,但代碼量通常龐大且晦澀難懂,難以裁剪定製,資源佔用也比較高。QuickJS 只要一個神人就能做到基本完整支持 ES2019,而 V8 則要一支 Google 的精英團隊才維護得動。另外,JIT 機制需要一個 warm up 的過程,這也是不能忽視的性能開銷。
可以說,在是否使用 JIT 之間的取捨,也比用 C++ 還是 C 來實現 Runtime 更重要。讓我們看看工業界的實例:
- Chrome 發布當年一騎絕塵地快,原因之一就在於 V8 這個工業級的 JIT 引擎,基本吊打當時 IE 的傳統 JS 解釋器。
- Facebook 最近宣布在 RN 上取得的性能進步,反而在於它用不帶 JIT 的 Hermes,替代了安卓默認的 V8——嵌入式的 JS 引擎降低了 JS 與 Native 通信的成本,並可以編譯到自身的位元組碼,提高 App 的載入速度(這裡 Facebook 玩了個花招,宣稱 Hermes 支持了 AOT 編譯。但這種編譯到位元組碼的 AOT,運行時是肯定不如 V8 編譯到機器碼的 JIT 來得快的)。
- V8 最近提供了一個 JIT-less 模式,可以關閉 JIT 來降低內存佔用。在 JIT-less 模式下面,運行計算密集型任務的效率慢了 10 倍以上,但真實世界裡 Web 應用場景的 Benchmark 水平則只慢了 40% 左右。
雖然腳本語言對 CPU 密集型的任務戰力不高,但打不過我可以叫大哥呀!在我熟悉的 Web 上,我個人看到了這樣的一種趨勢:嵌入式的腳本引擎和嵌入式的 WASM 原生代碼相輔相成,進一步增強了腳本世界和 Native 世界的聯繫。你可能發現我寫的是 JS 或 Python,但其中的每行代碼背後都是強力的原生代碼。腳本就像《攻殼機動隊》里那樣被「武裝」起來: