自然,守弱和Plan B

本文繼續細化這個討論:「道法自然」和守弱。

在前面這個討論中,有人提到,道法自然是順勢而為,而順勢而為是提前應對或者Plan B。而這恰恰是我反對的東西。所以我獨立寫一個文檔討論這個問題。

思考都是建立邏輯鏈的過程。設計也是個邏輯鏈,編碼也是邏輯鏈。都是「如果XX,就YY,如果ZZ,就KK」。引申一點,就心來說,戰術也是邏輯判斷,戰略也是邏輯判斷。在「腦力」的投入上,兩者沒有什麼不同。而一個人的腦力是有限的,無論在戰略上還是戰術上都要省著用,要平衡兩者的投資。戰術會直接產生結果,戰略可以降低戰術的成本。所以不做設計的結果是大量的代碼重寫,過度設計的結果是一直都在設計,產生不了代碼。設計和代碼有一個「自然」的位置,這個位置和這個事情的「內部應力」有關,和各個參與方的「度」有關。它可以摸出來,但無法「預設計」出來。

所以,首先順勢而為不是Plan B,順勢而為是刪除戰略邏輯鏈中「不需要投入自己的戰術精力」就可以實現的依賴,從而降低整個工作的成本。而Plan B的邏輯是增加風險管理計劃來提高戰略邏輯鏈的可靠性。這兩者是並列的兩個策略。更重要的,這兩者都不是萬能的,因為它們仍有可能過了那個「度」,無法咬到「道」真正會斷的位置。兩者都是有主動性在裡面,而不是讓「自然」自己去發生。

道法自然本身有兩個意象:

第一,道和個人慾望或者定義是偏離的,道不以意志而轉移,它是共同作用的結果

第二,道可道。道確實是有規律的,不是不可捉摸的(否則就沒有「法」這個概念了),但你不能去控制它,你只能去利用它。你不一定定義它的位置,但你可以「預期」它的範圍,或者在發展過程中跟隨它。簡單說,你不能提前畫出一隻貓的運動路線,但這不表示你在實際操作中不能跟著貓跑。

不能「控制」,卻可以「利用」,這個邏輯的應用價值是什麼呢?我們拿一個設計例子來深入探討一下這個結論。

前段時間,我們晶元開發組開發了一個特性,其中有一個寄存器是128位的,有底層開發經驗的讀者應該明白,這樣的IO寄存器處理起來是很麻煩的,因為一般的64位平台,一次可以發起的地址最多64位。一次發出一個128位的地址,需要比較特殊的SIMD指令才能搞定。我評審這個介面的時候就順口說了一句:「這種介面得在驅動裡面放彙編,又得跟社區撕逼了」。那位設計師就擔心起來了,立即說,「那我們把這個介面改成64位的。」,要說,他把這個介面改成64位,軟體部分肯定是容易處理多了。但我猜,既然他當初設計成128位的,顯然是為了解決原子性的問題,換成64位的,軟體不會變得更簡單,因為可能得在過程中加鎖,加了鎖,軟體就和線程庫發生關係,和線程庫發生關係,轉化為用戶態驅動就會再增加一堆的困難,說不定最終還得不償失呢?

作為一個軟體設計師或者一個硬體設計師,如果在面對問題的時候,有無數的解決方案,他反而會很難受。做事容易,選擇艱難。本質上,人在面對「設計」的時候,都是BDSM受虐狂,他們喜歡「限制」。正如那句話說的:錢的問題最好解決了——要不有錢,要不沒錢!

「沒得選擇」為什麼成為大部分人的追求?核心問題不就是我前面提到的「腦力成本」嘛。

這個位長的例子中,介面上有很多的選擇,但每個選擇都有成本,無論你做多麼細節的設計和推演,做多少個Plan B,你的預期都可能出錯。關鍵問題是,選擇產生的執行成本,和你計較Plan B,Plan C,Plan D的成本,都是一樣的成本。

比如128位的API,實際上Cavium已經在社區推writeo()介面,準備對這個彙編進行統一了。你可能覺得你可以賭這個「道」是最終128位IO會成為一種標準的平台功能。但這個補丁還沒有被接受呢!它是不是一定被接受?你在ARMv8上當然可以很容易做stp/ldp,在其他人的平台上是不是那麼容易被接受?再說了,為了讓其他人接受,這個實現也已經在退縮了,你看看這個討論:[PATCH RFC 0/3] API for 128-bit IO access,作者已經說了,這個操作可以不是原子的。那麼前面這個例子中,我們根本就不可以依賴這個API,因為我們整個目標就是通過這個操作來實現原子性。原子性才是我們的「弱」,「統一的128位IO API」不是。

所以,道法自然的核心在什麼地方呢?道法自然的核心在於,人心是控制不了「自然」的複雜度的,你無法千般計較,但它又確確實實有「規律」存在,你在一定程度上可以跟隨(而不是控制)。

所以,守弱才這麼重要,因為守在弱上,你面對變化的時候才有餘地,守在強上,只會增加額外的成本。

具體在這個例子上,我們首先應該抱最粗的一條大腿(也是我們的目標)進行設計,這個大腿顯然是:客戶需求。客戶要求這個功能成立,而且性能最高,他換一個選擇的唯一依據是有人提供更好的解決方案,否則道就會向著我的方向走。這是最基礎的邏輯。

然後我們就可以基於我們認識的最初步認識,綜合軟硬體的設計,用功能和高性能作為丈量做出一個,「最好」的,但「沒有經過那些沒有被『計較』的其他因素洗禮」的,介面。介面做成這樣,然後雙方進行溝通,背後軟體和硬體在自身約束上的考量可以表達出來,雙方對對方的「弱」是有所了解的,比如晶元的約束可能會是:

  1. IO的匯流排寬度是128位的,這個是已有的實現,不太可能修改成其他位長了
  2. IO的IO地址空間只分配了128M,要支持64個VF,無法擴大範圍了
  3. IO對內存的訪問都經過IOMMU單元,對內存訪問的原子性是守IOMMU的行為控制的,不可能越過
  4. 等等

軟體的約束可能會是:

  1. 軟體需要同時支持x86和ARM,增加128位原子操作不是不可能,但有額外成本
  2. 軟體要支持用戶態進程不經過系統調用直接使用VF,不能和其他VF實現互斥
  3. 軟體需要支持1萬個以上的進程共享使用這個IO,所以必須支持功能隔離和快速動態重新分配
  4. 等等

這些東西讓你列出來根本就列不完(我剛接手晶元軟體工作的時候,晶元的同事最喜歡讓我列出「軟體的所有要求」了:)),我們需要做的是完全按自己的期望把自己模塊的期望表達出來,包括自己錯誤的「認識」,不用計較那麼多(比如,「我就是提供128位的地址」,「我認為晶元實現這個判斷邏輯成本不高」),但交流的時候心中是守著這些真正的「弱」的,這樣在一番交流後,真正的約束就暴露出來了,雙方對對方的底線就清楚了。這樣就會越加地貼近「道」。這些,在交流前是不能預期的,在未來的執行中,也是無法預期的,但它存在你的心裡,並一定程度擴散到介面的相關方那裡。如果你一開始就是弱,既無法設計,也無法交流,實現不了真正「弱」的配合。但反過來,如果你守在強上,動不動就「不可更改」,「我的設計,怎麼可能錯」,「你不懂軟體」,「你不懂晶元」……那你終究只會失道。甚至,更常見的情況,你怕漏怯,乾脆避開那些明明是你的關鍵判斷,但你沒有把握的東西,你在對外交流中故意隱藏了你的關鍵依賴,「交流和合作」這件事本身在關鍵依賴上就幫助不了你,道最終就會backfire到你的頭上。求名失道,求道失名!

所以,「守弱」,既要強調守,也要強調弱。

哦,對了,還有一句在交流中我最討厭聽到的:「這個功能對軟體/晶元不可見」,這句話用在封裝性上是沒有問題的,但很多人是用在莫名的優越感上的:「你們這些外行不懂,這些問題不用管,用我告訴你的介面就可以了」。fuck you。你這麼牛,自己把功能就搞定好了,還來討論條毛啊?

——令人高興的是,至少我身邊這樣的越來越少了:)

p.s. 其實就著這個問題,還可以討論到開源戰略上。很多公司為了佔便宜,總覺得「我不用參與開源社區的活動,我把別人做得好的部分拿回家,我自己做得好的部分不用告訴他們」,他們覺得這樣簡直就把自己置於不敗之地。他們的決策層總是認為參與開源活動完全是參與「慈善活動」,是「社會責任」,卻從來都不明白,把自己包起來——已經在限制自己的能力了。

千般計較,不過是作繭自縛,不理解自然之道,人終究會以為人力的計較可以勝天,卻不知道,天道以無有入無間,你能封閉「有」,你是封閉不了「無有」的。


推薦閱讀:

TAG:軟體架構 | 軟體架構師 |