1. 程式人生 > >當我們追蹤原始碼時,要追蹤到什麼程度?

當我們追蹤原始碼時,要追蹤到什麼程度?

原始碼追蹤,是所有開發工程師要邁出的一道門檻。不會原始碼追蹤,或者不習慣於研究出色開源元件的原始碼,註定會限制自己的成長,也無法廣泛借鑑更多的程式設計技巧和工程思想。不會追蹤原始碼,就像是不會識字的文盲,沒辦法在海量的知識海洋中遨遊與成長。

前兩天看到一篇知乎熱文:《你都有哪些面試時被虐的經歷?》,以極其幽默的方式描述了在技術上被蹂躪的過程。究其原因,毫無疑問是基礎不牢固。但為什麼會基礎不牢固?無非就是在研究、鑽研技術時,沒有打破砂鍋問到底。但我們可以進一步細問,什麼叫做打破砂鍋問到底?要問到什麼程度才叫做到底了?

這幾乎就等價於,當你追蹤原始碼時,到底要追蹤到什麼程度?是追蹤到你當前所用框架的實現工具層?還是要追蹤到系統API呼叫層?又或是追蹤到你語言本身的實現層次?哪裡是個盡頭?應該到哪裡才是盡頭?

回顧上面那篇知乎熱文,其實有很多可以琢磨的細節。如果完全以虛心學習的態度來講,面對拷問細節的面試官,當然是要承認自己實力的不足,知恥而後勇地努力學習。但如果從另一個角度去思考,會體現出一些非常無奈的事實:我自己的工作中就用到這個程度,為什麼要問到如此細節?好不容易看了一下這個feature的實現,知道呼叫了系統底層的某個函式,你偏偏要問,那個系統函式是如何實現的。好不容易知道那個系統函式的實現思路,你非要問,在語言層面是什麼特性保證了該實現的合理性。

如此種種,到底何時是個頭?我到底應該追蹤原始碼追蹤到哪裡才算是一個盡頭?不是我不想打破砂鍋問到底,而是,要問到哪裡才是所謂的底(《用於深入思考的一些小工具》)?

這當然不是計算機領域才會存在的問題,每個領域可能都會有這樣一個元問題橫亙在菜鳥和高手之間。理解清楚這個“底”是什麼,幾乎就代表了你對這個學科最基本框架的透徹理解。

對於數學來講,這個問題其實不難解決,因為數學領域天生提供了一個最理想的“底”,叫做公理。凡是你的結論,無法歸結為公理的組合,你都叫做沒有問到底。任何的證明,要一步步地追蹤它的命題鏈條,一個個命題地追蹤,直到某個命題是由公理直接推匯出來,否則,你必須繼續去探尋這個命題的證明細節。

但即便是如此,也會有很多人在學習數學時,淺嘗輒止。例如,談論閉包時,會涉及到聚點,而聚點又和極限有關係。但很多初學者會止步於聚點這個貌似直觀的概念,而無法進一步去追問,我們這裡談論的聚點,到底是什麼意思。

也即是,打破砂鍋問到底這件事情,存在兩方面的難度,第一個是你必須界定好或者說知曉,你這裡的“底”是什麼。第二,你必須有意識地拷問自己,我的追問鏈條真的到底了嗎?如果這個底,沒辦法由整個領域裡最基本的幾個結論構成,那還叫做追問到底嗎?

回到剛開始的問題,當我們追蹤原始碼時,我們的“底”是什麼?而這個問題,等價於:在計算機領域,什麼才是整個大廈的元操作,或者說這個體系的公理體系?我們只需要掌握那幾個最小元操作的集合,就能夠推演出整個資訊科技大廈?如果我們能夠找到這個元操作集合,那我們的“底”也就顯而易見了,即,你追蹤程式碼時必須追蹤到這幾個元操作,才可以停止。否則,你就必須像追蹤數學的命題鏈那般,要進一步去研究每個操作的實現,直到某個feature是由計算機的元操作構成為止。

那麼,什麼是計算機的“元操作”公理體系呢?

如同探尋任何一個“最小存在集合”的問題,要理解最小的必不可少的元素,我們不妨反過來問一下自己,當什麼都沒有時,我會新增那些元素,來保證我要構建的產品的完整性。也即是:如果我來設計“計算機”這個產品,將會如何設計?

所謂:無需求,不設計。計算機這個產品要解決的問題是什麼?或者說,計算機的產品需求是什麼?如果按照圖靈的可計算來考慮,計算機要解決的磅礴需求無疑是:解決一切可計算的問題。

這個需求聽起來無疑是野心勃勃,無處下手。能夠承載“一切”可計算問題的解決方案,幾乎讓你感覺只有引入“上帝”這個外援,才能夠滿足產品的需求。“一切”是這個需求的變數,這個變數的變化程度是如此之大,以至於我們無處下手。那麼反過來,這個需求的不變數是啥?如同字面意思所言:可計算。

什麼叫做“可計算”的問題?顯然,就是通過“計算”能夠推匯出來的問題。那什麼是計算呢?無疑,它就是數學中最核心的概念,函式:使用一個輸入的數字,通過某種變換,吐出一個輸出的數字。如此,什麼是一切可計算的問題呢?就是一切可以通過“函式變換”推匯出來的問題,就可以稱之為可計算的問題。

(這裡其實還隱形地引入了一個必不可少的前提功能:儲存。因為你要接受輸入的數字,對其做變化,那麼中間必然需要有暫存結果的地方。就好比是筆算需要草稿紙,中間的步驟需要被儲存。)

顯然,下一個需要回答的問題便是:如果要描述“函式變換”,我們需要提供哪些功能?這或許需要我們再次藉助一點數學知識。在數學中,其初等函式是由“基本函式”(如四則運算、三角函式、指數函式等)的有限次組合構成,也即是,有了“基本函式”,只要能夠對它們進項有限次的分步操作,便能夠形成一個初等函式。而數學中的非初等函式,幾乎只限於符號上的推導以及邏輯上的操作。而要把它們具象為一個個可計算的實體,便只能通過初等函式的逼近,這基本上就是“計算數學”所要研究的核心。(例如,無論多麼高深的偏微分方程,如果落實到最後的計算上,它必須歸集為矩陣的運算,否則就是不可計算的。而如果強行要求解理論上不可計算的問題,也只能通過可計算的解法來做逼近。)

所以,這裡的核心便是,有了基本的數學函式,如四則運算、三角函式、指數函式,再把它們做有限次的組合,便能夠描述所有的“可計算”操作。稍微抽象一點,如果把基本函式看作是一條條的指令,那麼,這些指令的有限次組合,便可以描述所有的計算問題。

再根據計算領域的Böhm-Jacopini定理,任何複雜的指令集組合,都可以歸結為三種操作:基本指令操作、條件跳轉、迴圈。而無疑,條件跳轉、迴圈,也可以抽象成“指令操作”。也即是,只要提供了基本的數學指令集,條件跳轉、迴圈指令集,我們便能夠描述我們的“計算”行為。

(再一次的,既然是指令集,這同樣隱含了“儲存”指令集這個大前提。“儲存”和“計算”是密不可分的。並且,如果按照這裡的“指令集”描述計算行為的思考方式,functional programming中“把函式當做資料”的思想也就自然而然地出現了。為什麼說作為動詞的“function”可以被當做data來看待?因為它被歸結為了一堆指令集data的組合啊:=)!)

回頭考察我們最開始的需求“解決一切可計算的問題”,我們所設計的產品,計算機,只需要提供指令集的操作,以及它所隱含的“儲存”功能,便可以描述所有的計算問題,便能夠承載所有可計算問題的。對應到現代計算機,那便是“cpu+記憶體”的自然組合。

討論到這裡,似乎已經將需求全部滿足了。但如果進一步考慮“解決一切可計算的問題”這個需求本身,其實還有一點缺陷,就是計算和儲存,可以承載問題的解決方式,可是,問題從哪裡來呢?這有點像是,你有一顆充滿智慧的大腦,具備解決複雜問題的能力,但是,如果沒有眼睛、四肢、神經系統,你的這個“問題解決器”就成了一個自閉的一無是處的黑盒了。

所以,這裡的問題是什麼呢?就是你只具備解決問題的能力是不行的,你還必須有同外界互動(即吸收問題、突出解決方案)的能力。這便是I/O(input, output,輸入輸出的能力)這一模組的不可或缺性。有了I/O的能力,你所設計的計算機產品,便能夠得到最大限度的可擴充套件化,是滿足“一切”可計算問題的變數部分的重要元素。無論你的問題源自汽車引擎的引數除錯,還是源自商業物流的最優排程,又或者源自影象視訊的編輯,只要它們可以歸集為“可計算問題”,便能夠通過I/O模組,接入到計算機中去處理。於是,“一切”形形色色的問題,都可以通過I/O接入到你設計的這個小盒子中得到解決。這非常像人對問題的處理方式:一切問題,通過五官、四肢的感知,將資訊傳送給大腦。大腦經過神奇的思考(即計算),便能夠輸出解決方案。

如此,我們便得到了計算機的元操作公理體系,僅需要三部分:

  • 計算

  • 儲存

  • I/O

這個體系簡直漂亮得令人難以置信!而上面所討論的這個關於計算機的需求設計成果,便是大名鼎鼎的馮·諾依曼架構體系。

回到我們這篇文章的標題,當我們追蹤原始碼時,要追蹤到什麼程度?答案很簡單,你必須追蹤到這段程式碼,僅涉及“CPU基本指令集的操作、記憶體儲存的分配、I/O protocol介面呼叫”這幾個操作的組合時,你便可以說,我的程式碼就追蹤於此了,我算是打破砂鍋問到底了。

 


本文寫作得益於許世偉老師《許世偉架構體系》專欄的啟發,如果你也有興趣,不妨通過下面的邀請連結購買,也算是對本文的一種支援,謝謝!

 

 


 

 

近期回顧

《窮忙與第一宇宙速度》
《人生不是棋局,而是德撲》
《追回喪失的集中力》

 

如果你喜歡我的文章或分享,請長按下面的二維碼關注我的微信公眾號,謝謝!

 

26

 

   

更多資訊交流和觀點分享,可加入知識星球:

&nbs