1. 程式人生 > >JS是如何計算 1+1=2 的?

JS是如何計算 1+1=2 的?

 

身為程式設計師多年,作者今天突然對這件事感到十分好奇了。我問計算機芸芸部件,1+1究竟是如何計算的,他們都茫然的看著我。

 

開啟谷歌瀏覽器->Console面板,大腦向雙手不停傳送生物電訊號,肌肉細胞大量鈣分子游離到細胞外肌肉拉緊,鈣分子回到細胞內肌肉又鬆開,鈣分子來來回回進進出出多次,在大腦總樞紐指揮下,在多指配合下,最終完成了1、Shift+、1的鍵入,然後回車,輸出如下:

 

 

谷歌瀏覽器返回了2。

 

作者問瀏覽器:“你小子是怎麼知道1+1等於2的?縱觀人類進化史,從學會使用石頭,到學會結繩記數,用了100萬年。你年紀輕輕28歲,是怎麼知道1+1等於2的?”

 

瀏覽器說:“我不知道啊,是v8告訴我的。”

 

“v8是誰?是男是女?”

 

“非男非女,亦男亦女。v8是谷歌研發的JavaScript引擎,你發給我的JS程式碼,都是由他執行的。”

 

“把v8叫來,我有事問他。”

 

不一會兒,v8來到我面前。我問他:“你是怎麼知道1+1等於2的?人類世界上最聰明的孩子降生時,都不知道1+1是等於2的。你是怎麼知道的?”

 

“我並不知道1+1等於幾,我所有結果都是基於您的輸入給出的。”

 

作者問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這種的,你這種B8、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是進位結果。)

 

學過數學很容易理解,異或門的邏輯是這樣的:

負負得負、正負得正、負正得正、負負得正,這就是異或門邏輯。如果說異或閘電路有點複雜,那麼異或門又可以由與非門表示:

 

 

(讀者可以將1、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 的?