身爲程序員多年,做者今天忽然對這件事感到十分好奇了。我問計算機芸芸部件,1+1到底是如何計算的,他們都茫然的看着我。程序員
打開谷歌瀏覽器->Console面板,大腦向雙手不停發送生物電信號,肌肉細胞大量鈣分子游離到細胞外肌肉拉緊,鈣分子回到細胞內肌肉又鬆開,鈣分子來來回回進進出出屢次,在大腦總樞紐指揮下,在多指配合下,最終完成了一、Shift+、1的鍵入,而後回車,輸出以下:瀏覽器
谷歌瀏覽器返回了2。微信
做者問瀏覽器:「你小子是怎麼知道1+1等於2的?縱觀人類進化史,從學會使用石頭,到學會結繩記數,用了100萬年。你年紀輕輕28歲,是怎麼知道1+1等於2的?」優化
瀏覽器說:「我不知道啊,是v8告訴個人。」設計
「v8是誰?是男是女?」3d
「非男非女,亦男亦女。v8是谷歌研發的JavaScript引擎,你發給個人JS代碼,都是由他執行的。」orm
「把v8叫來,我有事問他。」視頻
不一下子,v8來到我面前。我問他:「你是怎麼知道1+1等於2的?人類世界上最聰明的孩子降生時,都不知道1+1是等於2的。你是怎麼知道的?」blog
「我並不知道1+1等於幾,我全部結果都是基於您的輸入給出的。」ip
做者問v8:「當瀏覽器把1+1發給你之後,你幹了啥?」
「我就是進行了詞法分析、語法分析,創建了語法樹、符號表等...」
瀏覽器說:「這些都不要說了,這都是身爲編譯器/解釋器的分內之事,你們都是這麼幹的,我解析Html標籤也是這麼幹的。直接說你解析完了幹了什麼?」
v8道:「解析完了,我就使用了MacroAssembler庫...」
瀏覽器說:"MacroAssembler庫就是縮寫爲masm的彙編庫吧,我去年在Strongtalk VM那裏見過他,要知道Strongtalk VM但是大大鼎鼎的Java虛擬機HotSpot JVM的前輩呢!Java但是比JS快多了!"瀏覽器顯得是一個見多識廣的人。要知道全世界的www網頁都展現都在他上面顯示,他真的是見多識廣。
但做者不喜歡瀏覽器自做聰明,「瀏覽器別打岔,v8你繼續講,使用masm幹了什麼?」
v8道:「masm提供了不少方法,基本和js是一一對應的,js語句是什麼,就調用對應的masm方法。例如1+1這名js代碼,對應調用masm的C++代碼是這樣的:
#define __ masm.
__ mov(eax, 1) //在這裏 __ 是一個宏,在預處理以後將被統一替換爲「masm.」。這一句是將寄存器eax設置爲1
__ add(eax, 1) //這一句將寄存器的值加1
__ ret(eax) //這裏返回寄存值的值
(以上只是示例,僞代碼不要當真)
上面是C++代碼,在內存裏生成機器碼大概長這個樣子:」
B8 01 00 00 00 ;mov eax,1
83 C0 01 ;add eax,1
瀏覽器道:「胡說!機器碼都是二進制格式010110010這種的,你這種B八、C0是什麼機器碼?」
v8道:「我沒有胡說啊!'B8 01 00 00 00'這是二進制機器碼的16進制展現,人類使用16進制更方便閱讀,但我和CPU交流都是以010110010這種二進制方式。」
這時CPU聽到有人叫他的名字,按耐不住了。CPU問:「誰在說俺的大名!v8,那後面的'mov eax,1'是什麼?我怎麼歷來沒見你提過?」
v8道:「'mov eax,1'是機器碼註釋,是給人類大哥看的,我給你看的都是二進制字節碼,是010110010這種。像mov它只是諸如1010這種彙編指令的代名詞,人類寫的是mov,彙編編譯器譯完就是1010了。
eax是寄存器地址,'mov eax,1'這句指令就是將寄存器的值設爲1。同時,它下面那句'add eax,1'是將寄存器的數值加1。add與mov不就是你的兩個指令嗎,CPU大哥?若是我發錯了指令,大哥歷來都未曾理會我。」
CPU點點頭。
瀏覽器繼續問:「好啊v8,用戶天天都罵我慢得像蝸牛,罪魁禍首原來在你這。碼農都說你快,我天天看你卻很慢。原來你是將js代碼先轉成了彙編代碼,再將彙編代碼轉成爲機器器,一件事轉二道手續,這樣能不慢嗎?爲何不直接將js代碼轉爲二進制機器碼交給CPU大哥執行?」
「哈哈哈」,v8大笑道:「瀏覽器,你只知表面,不知就理。js是解析型語言,如何直接編譯成機器碼?若是是這樣,它不就和Java同樣,是編譯型語言了嗎?」
瀏覽器反駁道:「雖然是解釋型語言,爲何不能先編譯再執行?在Java版JS解釋器rhino中,js腳本不是被編譯爲Java字節碼執行的嗎?」
做者以爲討論有點跑偏了,道:「言歸正傳。v8,瀏覽器給你的js代碼,你是讀一行調用masm轉化一行,仍是讀完了一塊兒調用masm再轉化的?」
v8說:「是一塊兒轉化的,但這一切都是在內存那裏折騰的。我有兩個助手,一個叫初級全碼編譯器(官名叫Full Code Generator),他將全部js代碼依次調用masm所有在內存中走了一遍;另外一個叫優化能手編譯器(官名叫Crankshaft),他針對運行屢次的代碼,以全碼編譯器的編譯結果爲基礎,再做一次優化編譯,目的是使代碼執行更快。」
這時內存說話了,「是啊,每次都把我折騰的暈頭轉向。別的exe文件,是先於我這裏加載、後交給CPU運行,一次搞定,很乾脆。惟有v8交給個人執行文件,連個名字都沒有,忽長忽短,變化莫測。」
CPU說:「可是我感受v8交給個人機器碼和普通的exe文件機器碼沒有什麼區別,在我這裏他們都是合法公民。只要機器指令所有正確,我就能返回正確的運行結果。」
看來v8並不知道1+1爲何等於2,v8爲了執行js快一點,大量佔用了內存空間,是用」空間換時間」的方法,博得了「v8引擎執行快」的美名。具體爲何1+1等於2,還須要問問CPU。
做者對CPU道:「CPU,你說只要指令正確,你就能返回正確結果。那麼v8將1+1的機器碼傳給你,你都作了什麼?」
CPU道:「報告主人,我什麼都沒有作。我作的一切,都是讓按照您的指令完成的。這一切都是您的智慧啊!」CPU態度很誠懇。
瀏覽器小聲道:「噓,馬屁精!」
CPU不理會,繼續說道:「首先,當我看到'mov eax 1',就知道這是叫我將值1移動到寄存器eax處。我有一個助理,叫指令指揮官,他負責指令的分類與調度。例如他看到指令是010100010010,首先從前4位0101判斷,這是一個寄存器設置命令,因而就打電話通知寄存器老頭來領取數據包裹;若是看到前4位是1010,就知道這是一個加法指令,就打電話通知算術運算單位的加法器來領取數據和任務,待加法器計算完了,他會將運算結果發給寄存器老頭保存。」
這時瀏覽器對CPU如何計算的也起了好奇,問道:「不要說人話,講機器語言,說人話咱們聽不懂。指令指揮官是如何給你的單位職員分派任務的?他看到0101,是怎麼知道應該分派給寄存器老頭的?」
「這麼簡單你都不明白嗎?好比0101這4個bit,依次表明4個路口,每一個路口有兩個岔,0向左轉,1向右轉,這樣0101一路走下來不就知道是哪一個職員負責了。」
指令分派確實簡單,關鍵還在加法器上。1+1等於幾是他算出來的,因而做者問道:「CPU,那加法器是如何計算1+1的呢?」
CPU道:「這就不那麼簡單了。加法器並不知道1+1等於幾。加法器是由半加器組成的,而半加器又是由一個異或門加一個與門組成的,以下所示是一個半加器:
(在上圖中,A、B是輸入,S是結果,C是進位結果。)
學過數學很容易理解,異或門的邏輯是這樣的:
負負得負、正負得正、負正得正、負負得正,這就是異或門邏輯。若是說異或門電路有點複雜,那麼異或門又能夠由與非門表示:
(讀者能夠將一、0不一樣值分別代入A、B,驗證異或門結果Q)
與非門的邏輯是這樣的:
負負得正、負正得正、正負得正、正正得負。與非門簡單電路能夠是這樣的:
x、y是兩個開關。x、y的開狀態爲1,關狀態爲0。x、y至關於與非門中的A、B。x、y狀態全開,以及任何一個狀態爲開,電路都是不通的。只有當x、y狀態全爲關,電路纔是通的。
與非門能夠由開關設計組成,異或門也能夠由開關組成。異或門加一個與門組成了半加器,多個半加器串到一塊兒,就組成了全加器,以下所示:
低位半加器的進位結果恰是高位半加器的輸入,合在一塊兒就組成了一個多位全加器。因此,個人加法運算能力也不是無限的,能算多大數字是由硬件決定的。」
這下明白了,CPU並不知道1+1等於2。之因此1+1能算出等於2,是人類在設計CPU的時候賦能給它的。而CPU內全部的運算,歸根結底又都是開關的開合。從這點來看,計算機的鼻祖居然是小小的開關。
瀏覽器問:「CPU,這樣說來你的加法器都是由衆多開關實現的。那減法運算、乘法運算、除法運算又是怎麼實現的?」
CPU道:「減法在我這裏也是加法,乘法是換算爲多位加法累加的,除法又能夠換算爲乘法。因此,全部四則運算都是由加法實現的。包括文字與音頻、視頻信息處理,在我這裏都是二進制的加減乘法與邏輯與非。」
瀏覽器又問:「那這樣說,在你內部肯家有不少不少的開關嘍?」
CPU說:「人類發明了一種雙極型三極管,簡稱晶體管。在我內部,晶體管很少,也就有幾十億個吧。每一個晶體管就至關於一個電路中的開關。」
原來做者在瀏覽器裏簡單敲一個1+1,CPU那裏就要噼裏啪啦開關個不停。
計算機並無智能。咱們從宏觀上看,彷彿計算機擁有了智能通常,能處理不少複雜的問題,其實都是經過數以億計的晶體管開關電路實現的,而且這種能力也都是人類賦予它的。
在人的大腦中,也有幾十億個神經元,像一個計算機同樣。人爲何擁有智能?或者人根本也並不擁有智能,在上帝那裏,咱們的大腦也只是按照他老人家的設計表現開頭狀態而已?
2018年12月21日於北京
藝覽無餘
首發於「藝述思惟」微信公衆號:JS是如何計算 1+1=2 的?