轉計算機語言的鉅變

譯文原地址程序員

http://blog.jobbole.com/113828/編程

英文原地址小程序

http://esr.ibiblio.org/?p=7724緩存

個人上一篇博文《與 C 語言長別離》引來了個人老朋友,一位 C++ 專家的評論。在評論裏,他推薦把 C++ 做爲 C 的替代品。這是不可能發生的,若是 C++ 代替 C 是趨勢的話,那麼 Go 和 Rust 也就不會出現了。安全

可是我不能只給個人讀者一個光禿禿的見解(LCTT 譯註:此處是雙關語)。因此,在這篇文章中,我來說述一下爲何我再也不碰 C++ 的故事。這是關於計算機語言設計經濟學專題文章的起始點。這篇文章會討論爲何一些真心很差的決策會被作出來,而後進入語言的基礎設計之中,以及咱們該如何修正這些問題。數據結構

在這篇文章中,我會一點一點的指出人們(固然也包括我)自從 20 世紀 80 年代以來就存在的關於將來的編程語言的預見失誤。直到最近,咱們才找到了證實咱們錯了的證據。併發

我記得我第一次學習 C++ 是由於我須要使用 GNU eqn 輸出 MathXML,而 eqn 是使用 C++ 寫的。那個項目不錯。在那以後,21 世紀初,我在韋諾之戰Battle For Wesnoth那邊當了多年的資深開發人生,而且與 C++ 相處甚歡。編程語言

在那以後啊,有一天咱們發現一個不當心被咱們授予提交權限的人已經把遊戲的 AI 核心搞崩掉了。顯然,在團隊中只有我是不那麼懼怕查看代碼的。最終,我把一切都恢復正常了 —— 我折騰了整整兩週。再那以後,我就發誓我不再靠近 C++ 了。工具

在那次經歷事後,我發現這個語言的問題就是它在嘗試使得原本就複雜的東西更加複雜,來粗陋補上由於基礎概念的缺失形成的漏洞。對於裸指針這樣東西,它說「別這樣作」,這沒有問題。對於小規模的我的項目(好比個人魔改版 eqn),遵照這些規定沒有問題。性能

可是對於大型項目,或者開發者水平良莠不齊的多人項目(這是我常常要處理的狀況)就不能這樣。隨着時間的推移以及代碼行數的增長,有的人就會捅簍子。當別人指出有 BUG 時,由於諸如 STL 之類的東西給你增長了一層複雜度,你處理這種問題所須要的精力就比處理同等規模的 C 語言的問題就要難上不少。我在韋諾之戰時,我就知道了,處理這種問題真的至關棘手。

我給 Stell Heller(個人老朋友,C++ 的支持者)寫代碼時不會發生的問題在我與非 Heller 們合做時就被放大了,我和他們合做的結局可能就是我得給他們擦屁股。因此我就不用 C++ ,我以爲不值得爲了其花時間。 C 是有缺陷的,可是 C 有 C++ 沒有的優勢 —— 若是你能在腦內模擬出硬件,那麼你就能很簡單的看出程序是怎麼運行的。若是 C++ 真的能解決 C 的問題(也就是說,C++ 是類型安全以及內存安全的),那麼失去其透明性也是值得的。可是,C++ 並無這樣。

咱們判斷 C++ 作的還不夠的方法之一是想象一個 C++ 已經搞得不錯的世界。在那個世界裏,老舊的 C 語言項目會被遷移到 C++ 上來。主流的操做系統內核會是 C++ 寫就,而現存的內核實現,好比 Linux 會漸漸升級成那樣。在現實世界,這些都沒有發生。C++ 不只沒有打消語言設計者設想像 D、Go 以及 Rust 那樣的新語言的想法,它甚至都沒有取代它的前輩。不改變 C++ 的核心思想,它就沒有將來,也所以,C++ 的抽象泄露leaky abstraction也不會消失。

既然我剛剛提到了 D 語言,那我就說說爲何我不把 D 視爲一個夠格的 C 語言競爭者的緣由吧。儘管它比 Rust 早出現了八年(和 Rust 相比是九年)Walter Bright 早在那時就有了構建那樣一個語言的想法。可是在 2001 年,以 Python 和 Perl 爲首的語言的出現已經肯定了,專有語言能和開源語言抗衡的時代已通過去。官方 D 語言庫/運行時和 Tangle 的無謂紛爭也打擊了其發展。它從未修正這些錯誤。

而後就是 Go 語言(我原本想說「以及 Rust」。可是如前文所述,我認爲 Rust 還須要幾年時間纔能有競爭力)。它的確是類型安全以及內存安全的(好吧,是在大多數時候是這樣,可是若是你要使用接口的話就不是如此了,可是自找麻煩可不是正常人的作法)。個人一位好友,Mark Atwood,曾指出過 Go 語言是脾氣暴躁的老頭子由於憤怒而創造出的語言,主要是 C 語言的做者之一(Ken Thompson) 由於 C++ 的混亂臃腫形成的憤怒,我深覺得然。

我能理解 Ken 惱火的緣由。這幾十年來我就一直認爲 C++ 搞錯了須要解決的問題。C 語言的後繼者有兩條路可走。其一就是 C++ 那樣,接受 C 的抽象泄漏、裸指針等等,以保證兼容性。而後以此爲基礎,構建一個最早進的語言。還有一條道路,就是從根源上解決問題 —— 修正 C語言的抽象泄露。這一來就會破環其兼容性,可是也會杜絕 C/C++ 現有的問題。

對於第二條道路,第一次嚴謹的嘗試就是 1995 年出現的 Java。Java 搞得不錯,可是在語言解釋器上構建這門語言使其不適合系統編程。這就在系統編程那留下一個巨大的洞,在 Go 以及 Rust 出現以前的 15 年裏,都沒有語言來填補這個空白。這也就是個人 GPSD 和 NTPsec 等軟件在 2017 年仍然主要用 C 寫成的緣由,儘管 C 的問題也不少。

在許多方面這都是很糟糕的狀況。儘管因爲缺乏足夠多樣化的選擇,咱們很難認識到 C/C++ 作的不夠好的地方。咱們都認爲在軟件裏面出現缺陷以及基於安全方面考慮的妥協是理所固然的,而不是想一想這其中多少是真的因爲語言的設計問題致使的,就像緩存區溢出漏洞同樣。

因此,爲何咱們花了這麼長時間纔開始解決這個問題?從 C 1972 年面世到 Go 2009 年出現,這其中隔了 37 年;Rust 也是在其僅僅一年以前出現。我想根本緣由仍是經濟。

從最先的計算機語言開始,人們就已經知道,每種語言的設計都體現了程序員時間與機器資源的相對價值的權衡。在機器這端,就是彙編語言,以及以後的 C 語言,這些語言以犧牲開發人員的時間爲代價來提升性能。 另外一方面,像 Lisp 和(以後的)Python 這樣的語言則試圖自動處理儘量多的細節,但這是以犧牲機器性能爲代價的。

廣義地說,這兩端的語言的最重要的區別就是有沒有自動內存管理。這與經驗一致,內存管理缺陷是以機器爲中心的語言中最多見的一類缺陷,程序員須要手動管理資源。

當相對價值斷言與軟件開發在某個特定領域的實際成本動因相匹配時,這個語言就是在經濟上可行的。語言設計者經過設計一個適合處理如今或者不遠的未來出現的狀況的語言,而不是使用現有的語言來解決他們遇到的問題。

隨着時間的推移,時興的編程語言已經漸漸從須要手動管理內存的語言變爲帶有自動內存管理以及垃圾回收(GC)機制的語言。這種變化對應了摩爾定律致使的計算機硬件成本的下降,使得程序員的時間與以前相比更加的寶貴。可是,除了程序員的時間以及機器效率的變化以外,至少還有兩個維度與這種變化相關。

其一就是距離底層硬件的距離。底層軟件(內核與服務代碼)的低效率會被成倍地擴大。所以咱們能夠發現,以機器爲中心的語言向底層推動,而以程序員爲中心的語言向着高級發展。由於大多數狀況下面向用戶的語言僅僅須要以人類的反應速度(0.1 秒)作出迴應便可。

另外一個維度就是項目的規模。因爲程序員抽象發生的問題的漏洞以及自身的疏忽,任何語言都會有可預期的每千行代碼的出錯率。這個比率在以機器爲中心的語言上很高,而在程序員爲中心的帶有 GC 的語言裏就大大下降。隨着項目規模的增大,帶有 GC 的語言做爲一個防止出錯率不堪入目的策略就顯得愈發重要起來。

當咱們使用這三種維度來看當今的編程語言的形勢 —— C 語言在底層,蓬勃發展的帶有 GC 的語言在上層,咱們會發現這基本上很合理。可是還有一些看似不合理的是 —— C 語言的應用不合理地普遍。

我爲何這麼說?想一想那些經典的 Unix 命令行工具吧。那些小程序一般均可以使用帶有完整的 POSIX 支持的腳本語言快速實現出來。從新編碼那些程序將使得它們調試、維護和拓展起來都會更加簡單。

可是爲何仍是使用 C (或者某些像 eqn 的項目,使用 C++)?由於有轉換成本。就算是把至關小、至關簡單的程序使用新的語言重寫而且確認你已經忠實地保留了全部非錯誤行爲都是至關困難的。籠統地說,在任何一個領域的應用編程或者系統編程在一種語言的權衡過期以後,仍然堅持使用它。

這就是我和其餘預測者犯的大錯。 咱們認爲,下降機器資源成本(增長程序員時間的相對成本)自己就足以取代 C 語言(以及沒有 GC 的語言)。 在這個過程當中,咱們有一部分或者甚至一大部分都是錯誤的 —— 自 20 世紀 90 年代初以來,腳本語言、Java 以及像 Node.js 這樣的東西的興起顯然都是這樣興起的。

可是,競爭系統編程語言的新浪潮並不是如此。 Rust 和 Go 都明確地迴應了增長項目規模 這一需求。 腳本語言是先是做爲編寫小程序的有效途徑,並逐漸擴大規模,而 Rust 和 Go 從一開始就定位爲減小大型項目中的缺陷率。 好比 Google 的搜索服務和 Facebook 的實時聊天覆用。

我認爲這就是對 「爲何再也不早點兒」 這個問題的回答。Rust 和 Go 實際上並不算晚,它們相對迅速地迴應了一個直到最近才被發現低估的成本動因問題。

好,說了這麼多理論上的問題。按照這些理論咱們能預言什麼?它告訴咱們在 C 以後會出現什麼?

推進 GC 語言發展的趨勢尚未扭轉,也不要期待其扭轉。這是大勢所趨。所以:最終咱們擁有具備足夠低延遲的 GC 技術,可用於內核和底層固件,這些技術將以語言實現方式被提供。 這些纔是真正結束 C 長期統治的語言應有的特性。

咱們能從 Go 語言開發團隊的工做文件中發現端倪,他們正朝着這個方向前進 —— 可參見關於併發 GC 的學術研究 —— 從未中止研究。 若是 Go 語言本身沒有選擇這麼作,其餘的語言設計師也會這樣。 但我認爲他們會這麼作 —— 谷歌推進他們的項目的能力是顯而易見的(咱們從 「Android 的發展」就能看出來)。

在咱們擁有那麼理想的 GC 以前,我把能替換 C 語言的賭注押在 Go 語言上。由於其 GC 的開銷是能夠接受的 —— 也就是說不僅是應用,甚至是大部份內核外的服務均可以使用。緣由很簡單: C 的出錯率無藥可醫,轉化成本還很高。

上週我嘗試將 C 語言項目轉化到 Go 語言上,我發現了兩件事。其一就是這活很簡單, C 的語言和 Go 對應的很好。還有就是寫出的代碼至關簡單。因爲 GC 的存在以及把集合視爲首要的數據結構,人們會預期代碼減小,可是我意識到我寫的代碼比我最初指望的減小的更多,比例約爲 2:1 —— 和 C 轉 Python 相似。

抱歉吶,Rust 粉們。大家在內核以及底層固件上有着美好的將來,可是大家在別的 C 領域被 Go 壓的很慘。沒有 GC ,再加上難以從 C 語言轉化過來,還有就是 API 的標準部分仍是不夠完善。(個人 select(2) 又哪去了啊?)。

對大家來講,惟一的安慰就是,C++ 粉比大家更糟糕 —— 若是這算是安慰的話。至少 Rust 還能夠在 Go 顧及不到的 C 領域內大展宏圖。C++ 可不能。

相關文章
相關標籤/搜索