前言: 吳思先生在《潛規(guī)則》(中國(guó)歷史中的真實(shí)游戲)一書中講述了很多生動(dòng)、有趣的官場(chǎng)故事,透過(guò)歷史表象,揭示出隱藏在正式規(guī)則之下、實(shí)際上支配著社會(huì)運(yùn)行的不成文的規(guī)矩, 非常值得閱讀。 和碼農(nóng)翻身公眾號(hào)之前的文章不同, 這是一篇沒(méi)有故事,讀起來(lái)不那么好玩的超級(jí)干貨, 我建議你靜下心來(lái),閱讀一遍, 仔細(xì)的思考一下, 絕對(duì)物超所值。 1. 上帝的規(guī)矩:局部性原理 這個(gè)原理講的是在一段時(shí)間里, 整個(gè)程序的執(zhí)行僅限于程序的某一個(gè)部分, 相應(yīng)的, 程序訪問(wèn)的存儲(chǔ)空間也局限于某一個(gè)內(nèi)存區(qū)域, 具體分為: (1) 時(shí)間局部性:是指如果程序中的某條指令一旦執(zhí)行,則不久之后該指令可能再次被執(zhí)行; 如果某數(shù)據(jù)被訪問(wèn),則不久之后該數(shù)據(jù)可能再次被訪問(wèn)。 (2) 空間局部性: 是指一旦程序訪問(wèn)了某個(gè)存儲(chǔ)單元,則不久之后。其附近的存儲(chǔ)單元也將被訪問(wèn)。 為什么是這樣? 我也不知道, 可能是計(jì)算機(jī)界的上帝定下的規(guī)矩。 但是這個(gè)原理的用處可就大了, 例如Java 虛擬機(jī), 本來(lái)是解釋執(zhí)行.class 文件,性能不怎么樣, 但是利用局部性原理, 就可以找到那些常用的, 所謂的熱點(diǎn)(Hotspot)代碼, 然后把他們編譯成本地原生代碼(native code), 這樣執(zhí)行效率就和C/C++差不多了。 當(dāng)然這個(gè)原理更大的用處就是下面要提到的緩存。 2. 坐飛機(jī)的怎么和坐驢車的打交道: 緩存 如果CPU每次做事的時(shí)候, 都等著內(nèi)存和硬盤, 那整個(gè)計(jì)算機(jī)的速度估計(jì)要慢的要死了。 所以根據(jù)局部性原理, 操作系統(tǒng)會(huì)把經(jīng)常需要用的數(shù)據(jù)從硬盤取到內(nèi)存, CPU 會(huì)把經(jīng)常用的數(shù)據(jù)從內(nèi)存取到自己的緩存中。 (參見(jiàn)文章《CPU阿甘》) 通過(guò)這種辦法等待的問(wèn)題能帶到極大的緩解。 在Web 開(kāi)發(fā)中,緩存更是非常常見(jiàn)的, 由于數(shù)據(jù)庫(kù)(硬盤)太慢, 大部分Web系統(tǒng)都會(huì)把最常用的業(yè)務(wù)數(shù)據(jù)放到內(nèi)存中緩存起來(lái)。 3. 拋棄細(xì)節(jié): 抽象 當(dāng)我們遇到復(fù)雜問(wèn)題的時(shí)候, 抽象是非常重要的武器。 《深入理解計(jì)算機(jī)系統(tǒng)》一書中提到: “指令集是對(duì)CPU的抽象, 文件是對(duì)輸入/輸出設(shè)備的抽象, 虛擬存儲(chǔ)器是對(duì)程序存儲(chǔ)的抽象, 進(jìn)程是對(duì)一個(gè)正在運(yùn)行的程序的抽象, 而虛擬機(jī)是對(duì)整個(gè)計(jì)算機(jī)(包括操作系統(tǒng)、處理器和程序)的抽象?!薄?總結(jié)的非常精辟 CPU集成電路硬件無(wú)比復(fù)雜, 但是我們寫程序肯定不用接觸這些硬件細(xì)節(jié), 那樣就累死了, 我們只要遵循CPU的指令集, 程序就可以正確的運(yùn)行, 而不用關(guān)心指令在硬件層次到底是怎么運(yùn)行的。 硬盤也是這樣, 有磁道,柱面,扇區(qū), 我們寫應(yīng)用層程序也不用和這些煩人的細(xì)節(jié)打交道, 在操作系統(tǒng)和設(shè)備驅(qū)動(dòng)的配合下, 我們只需要面對(duì)一個(gè)個(gè)“文件”,打開(kāi),讀取,關(guān)閉就行了。 操作系統(tǒng)會(huì)把邏輯的文件翻譯成物理磁盤上的字節(jié)。 再比如為了實(shí)現(xiàn)數(shù)據(jù)的共享,數(shù)據(jù)的一致性和安全性,需要大量的,復(fù)雜的程序代碼來(lái)實(shí)現(xiàn), 每個(gè)應(yīng)用程序都實(shí)現(xiàn)一份肯定不是現(xiàn)實(shí)的。 所以計(jì)算機(jī)科學(xué)抽象出了一個(gè)叫數(shù)據(jù)庫(kù)的東西, 你只需要安裝數(shù)據(jù)庫(kù)軟件, 使用SQL和事務(wù)就能實(shí)現(xiàn)多用戶對(duì)數(shù)據(jù)的安全訪問(wèn)了。 參見(jiàn)文章《抽象, 程序員必備的能力》 4. 我只想和鄰居打交道: 分層 分層其實(shí)也是抽象的一種,它通過(guò)層次把復(fù)雜的,可能變化的東西隔離開(kāi)來(lái), 某一層只能訪問(wèn)它的直接上層和下層, 不能跨層訪問(wèn)。 例如網(wǎng)絡(luò)協(xié)議分層: ![]() 再比如Web開(kāi)發(fā)的分層: ![]() 分層的好處就是隔離變化, 在接口不變的情況下, 某一層的變化只會(huì)局限于本層次內(nèi)。 即使是接口變化, 也僅僅會(huì)影響調(diào)用方。 5. 我怕等不及: 異步調(diào)用 當(dāng)你的程序需要等待一個(gè)長(zhǎng)時(shí)間的操作而被阻塞住時(shí)而無(wú)所事事的時(shí)候, 異步調(diào)用就派上用場(chǎng)了。 異步調(diào)用簡(jiǎn)單就是說(shuō): 我等不及你了, 先去做別的事情, 你做完了告訴我一聲。 回到最早的那個(gè)CPU的例子, CPU速度太快, 當(dāng)它想讀取硬盤文件的時(shí)候,是不會(huì)等待慢1000多萬(wàn)倍的硬盤的, 它會(huì)啟動(dòng)一個(gè)DMA , 不用通過(guò)CPU, 直接把數(shù)據(jù)從硬盤讀到內(nèi)存, 讀完以后通過(guò)中斷的方式來(lái)通知CPU。 Node.js 和 Web服務(wù)器Nginx 也是這樣, 一個(gè)線程或若干個(gè)線程處理所有的請(qǐng)求, 遇到耗時(shí)的操作, 絕不等待, 馬上去干別的事情,等到耗時(shí)操作完成后,再來(lái)通知這些干活的線程。 還有著名的AJAX , 當(dāng)瀏覽器中的javascript發(fā)出一個(gè)Http 請(qǐng)求的時(shí)候, 也不會(huì)等待從服務(wù)器端返回?cái)?shù)據(jù), 只是設(shè)置一個(gè)回調(diào)函數(shù), 服務(wù)器響應(yīng)數(shù)據(jù)回來(lái)的時(shí)候調(diào)用一下就行了。 6. 大事化小, 小事花了 : 分而治之 分而治之的基本思想是將一個(gè)規(guī)模比較大的問(wèn)題分解為多個(gè)規(guī)模較小的子問(wèn)題,這些子問(wèn)題相互獨(dú)立且與原問(wèn)題性質(zhì)相同。求出子問(wèn)題的解,最后組合起來(lái)就可得到原問(wèn)題的解。 由于子問(wèn)題和原問(wèn)題性質(zhì)相同, 所以很多時(shí)候可以用遞歸。 歸并排序就是一個(gè)經(jīng)典的例子, 數(shù)據(jù)結(jié)構(gòu)與算法書上到處都是, 這里就不在贅述了。 如果把分而治之泛化一下, 到軟件設(shè)計(jì)領(lǐng)域, 就可以認(rèn)為是把一個(gè)大問(wèn)題逐步分解的過(guò)程: ![]() |
|
來(lái)自: 子歌-特斯拉 > 《程序設(shè)計(jì)》