摘要:代碼在本文最后,首先是,編譯出字節(jié)碼耗時(shí)約,運(yùn)行字節(jié)碼耗時(shí)約,。也有解釋過程,字節(jié)碼需要由虛擬機(jī)解釋執(zhí)行。而引擎的做法是更接近二哥的,在編譯階段的過程是源碼抽象語法樹字節(jié)碼中間代碼。于是大量的字節(jié)碼優(yōu)化措施被延后,比如。
簡(jiǎn)單性能測(cè)試
首先,我們先來做一個(gè)簡(jiǎn)單的性能測(cè)試,對(duì)比一下Java,JavaScript,PHP,Ruby這四門語言。這個(gè)性能測(cè)試,是計(jì)算斐波那契數(shù)列(兔子數(shù)列)。比如計(jì)算n=5的兔子數(shù)列,結(jié)果是:1,1,2,3,5,8,13,21,34,55(1+1=2...21+34=35)。
這很容易通過一個(gè)遞歸來實(shí)現(xiàn),JavaScript代碼如下:
function fs(n) { if (n <= 2) { return 1; } else { return fs(n - 1) + fs(n - 2); } }
可以看出,這個(gè)測(cè)試主要偏重CPU棧操作。
以上面這個(gè)函數(shù)為基礎(chǔ),加上一些邏輯,分別使用Java,JavaScript,PHP,Ruby這四門語言編寫了腳本,計(jì)算n=40的兔子數(shù)列,我們看一下結(jié)果吧。(代碼在本文最后,^_^)
首先是Java,編譯出字節(jié)碼耗時(shí)約1s,運(yùn)行字節(jié)碼耗時(shí)約1s,666。
其次是JavaScript,在node環(huán)境下運(yùn)行耗時(shí)約3.5s,在瀏覽器環(huán)境(Safari)下約8s,66。
接著是Ruby,出人意料的結(jié)果,約39s,6不起來了。
最后是PHP,約80s,233。
C或者C++的代碼我沒有寫,肯定跑得比狗還快。
這個(gè)簡(jiǎn)單性能測(cè)試并不能說明語言優(yōu)劣,只是比較好玩而已,代碼在本文最后,有興趣可以去運(yùn)行一下。
Java鶴立雞群的原因 靜態(tài)類型vs動(dòng)態(tài)類型靜態(tài)類型語言指的是編譯的時(shí)候就能夠知道每個(gè)變量的類型,我們編程的時(shí)候當(dāng)然也需要給定類型,如Java中的整型int,浮點(diǎn)型float等。
動(dòng)態(tài)類型語言指的是運(yùn)行的時(shí)候才能夠知道每個(gè)變量的類型,編程的時(shí)候也無需顯示指定類型,如JavaScript中的var,PHP中的$。
看上去,靜態(tài)類型還是動(dòng)態(tài)類型對(duì)性能沒什么影響,實(shí)際上卻影響很大。
概括來說就是,靜態(tài)類型語言在編譯后會(huì)大量利用類型已知的優(yōu)勢(shì),比如int類型,占用4個(gè)字節(jié),編譯后的代碼就可以使用內(nèi)存地址加偏移量的方法存取變量。而地址+偏移量的算法匯編非常容易實(shí)現(xiàn)。
那動(dòng)態(tài)類型語言是如何做的呢?概括的來說就是當(dāng)做字符串通通存下來,之后存取就用字符串匹配。
可以感受到這兒存在的性能差異了嗎?
編譯型vs解釋性編譯型語言,就像C/C++,代碼要經(jīng)過編譯器編譯成可執(zhí)行程序后才可以運(yùn)行。這個(gè)編譯過程沒什么時(shí)間要求,所以編譯器可以做大量代碼優(yōu)化措施,有時(shí)候編譯要好久好久。
解釋型語言,就像JavaScript,就是引擎直接讀源碼,然后就出結(jié)果,當(dāng)然這樣子做效率非常低。就像靠人腦去讀源碼,然后寫答案一樣。
奇葩型語言,就像Java,有編譯過程,但編譯產(chǎn)出的是中間代碼(字節(jié)碼),這個(gè)過程也有充分的時(shí)間做優(yōu)化。也有解釋過程,字節(jié)碼需要由Java虛擬機(jī)解釋執(zhí)行。
從這兒,大概可以理解,為什么C/C++運(yùn)行效率比Java更高。因?yàn)椴还茉趺凑f,直接運(yùn)行二進(jìn)制碼都比解釋執(zhí)行字節(jié)碼來得快吧。
所以,有趣的事情就來了,C/C++是大哥,Java是二哥,一群解釋型腳本語言是小弟們。大哥,獨(dú)孤求敗。二哥,想法子和大哥站在一條線上。小弟們,盡全力跟上二哥。
現(xiàn)代JavaScript引擎的努力先來看看,Java虛擬機(jī)做了哪些努力?
Java想的肯定是優(yōu)化虛擬機(jī)解釋執(zhí)行字節(jié)碼的速度,這兒正是和大哥拉開差距的地方。從大哥那學(xué)了很多招。其中重要的一招就是JIT(Just-In-Time),主要的思想就是解釋器在解釋字節(jié)碼的時(shí)候,會(huì)將部分字節(jié)碼轉(zhuǎn)化成本地代碼(匯編代碼),這樣可以被CPU直接執(zhí)行,而不是解釋執(zhí)行,從而極大地提高性能。
重點(diǎn)來看看,JavaScript引擎做了哪些努力?
JavaScript從前輩那里學(xué)習(xí)了很多,總結(jié)來說有:
優(yōu)化數(shù)據(jù)表示,彌補(bǔ)動(dòng)態(tài)類型的性能缺陷
引入一個(gè)編譯過程,而不是直接解釋執(zhí)行,但這個(gè)編譯過程和運(yùn)行是一起的,時(shí)間的權(quán)衡變得非常重要。
JIT技術(shù),與Java中的JIT原理相同
V8引擎與JavaScriptCore引擎各個(gè)JavaScript優(yōu)化的具體實(shí)現(xiàn)不太一樣。
舉例子來說,V8引擎對(duì)于編譯和JIT的做法是,在編譯階段的過程是:源碼=》抽象語法樹=》本地代碼。其中從抽象語法樹到本地代碼的過程使用的是JIT全碼生成器,其作用是將抽象語法樹轉(zhuǎn)換成各個(gè)硬件平臺(tái)和直接運(yùn)行的本地代碼。V8引擎的這種思路看起來像想要越過二哥Java,直接學(xué)大哥C/C++啊。
而JavaScriptCore引擎的做法是更接近二哥的,在編譯階段的過程是:源碼=》抽象語法樹=》字節(jié)碼(中間代碼)。對(duì)這個(gè)階段像極了二哥Java的編譯過程,只是這里小弟可沒有充裕的時(shí)間做優(yōu)化。于是大量的字節(jié)碼優(yōu)化措施被延后,比如JIT。JavaScriptCore引擎使用DFG JIT、LLVM等繼續(xù)對(duì)字節(jié)碼做優(yōu)化。
權(quán)衡時(shí)間很重要,一個(gè)很好的優(yōu)化措施但耗時(shí)太多,引入之后反而讓JavaScript整體的運(yùn)行時(shí)間變長(zhǎng)了,得不償失。另外,還有許多人提出,要不要完全抄二哥的,就是也引入一個(gè)提前編譯的過程,233
Ruby、PHP為什么在前面的測(cè)試中落敗具體原因可能還是在引擎吧,可能它們的引擎遠(yuǎn)沒有像V8這么努力。
總結(jié)首先,對(duì)于底層的理解,有助于編寫上層的代碼。比如現(xiàn)在我們?nèi)ダ斫釰avaScript代碼的時(shí)候,會(huì)更深刻。具體可以看這篇文章試試,《通過這一段代碼,讓我們重新認(rèn)識(shí)JavaScript》。
其次,多一些話題吧,比如以后和同伴談起V8引擎(裝B)的時(shí)候,說我這個(gè)例子還不錯(cuò)吧。
import java.util.Date; public class Fbnq { public static void main(String []args) { int num = 40; long startTime = new Date().getTime(); //System.out.println(startTime); String result = fslog(num); long endTime = new Date().getTime(); //System.out.println(endTime); float needTme = (endTime - startTime)/1000; System.out.println("time:"+needTme+"s,result:"+result); } public static int fs (int n){ if(n <= 2){ return 1; }else{ return fs(n-1)+fs(n-2); } } public static String fslog(int num){ String rsString = ""; for(int i=1;i<=num;i++){ int rs = fs(i); System.out.println(rs); if(i == 1){ rsString = rsString + rs; }else{ rsString = rsString + "," + rs; } } return rsString; } }JavaScript
var num = 40; var startDate = new Date().getTime(); var result = logfs(num); var endDate = new Date().getTime(); console.log("time:" + ((endDate - startDate) / 1000) + "s", "result:" + result); function logfs(num) { var rsString = ""; for (var i = 1; i <= num; i++) { var rs = fs(i); if (i === 1) { rsString = rsString + rs; } else { rsString = rsString + "," + rs; } console.log(rs); } return rsString; function fs(n) { if (n <= 2) { return 1; } else { return fs(n - 1) + fs(n - 2); } } }Ruby
def fs (n) if n < 2 return 1; else return (fs (n-1)) + (fs (n-2)); end end def fslog (num) num = num - 1; rsString = ""; for i in 1..num rs = fs i; puts rs; if i === 1 rsString = rsString + "#{rs}"; else rsString = rsString + ",#{rs}"; end end return rsString; end num = 40; startTime = Time.now.to_f*1000; rsString = fslog num; endTime = Time.now.to_f*1000; needTime = (endTime - startTime)/1000; puts "time:#{needTime}s,result:#{rsString}";Php 參考
《你所不知道的JavaScript(上卷)》
《WebKit技術(shù)內(nèi)幕》
《深入淺出Node.js》
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/79492.html
摘要:性能測(cè)試工具操作測(cè)試代碼測(cè)試結(jié)果黃條代表每秒可執(zhí)行測(cè)試函數(shù)的次數(shù),當(dāng)然越多越快。務(wù)必減少操作,減少無意義的路徑的查找。其他測(cè)試測(cè)試代碼這些測(cè)試代碼來自,官網(wǎng)的例子這是一個(gè)空的非循環(huán)測(cè)試。正則表達(dá)式測(cè)試,數(shù)組的,新建日期,稍微慢了些。 性能測(cè)試工具 JSLitmus dom操作 測(cè)試代碼 ...
摘要:本章將會(huì)深入谷歌引擎的內(nèi)部結(jié)構(gòu)。一個(gè)引擎可以用標(biāo)準(zhǔn)解釋程序或者即時(shí)編譯器來實(shí)現(xiàn),即時(shí)編譯器即以某種形式把解釋為字節(jié)碼。引擎的由來引擎是由谷歌開源并以語言編寫。注意到?jīng)]有使用中間字節(jié)碼來表示,這樣就不需要解釋器了。 原文請(qǐng)查閱這里,略有刪減。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第二章。 本章將會(huì)深入谷歌 V8 引擎的內(nèi)部結(jié)構(gòu)。我們也會(huì)...
摘要:引擎可以用標(biāo)準(zhǔn)解釋器或即時(shí)編譯器來實(shí)現(xiàn),即時(shí)編譯器以某種形式將代碼編譯為字節(jié)碼。這里的主要區(qū)別在于不生成字節(jié)碼或任何中間代碼。請(qǐng)注意,不使用中間字節(jié)碼表示法,不需要解釋器。這允許在正常執(zhí)行期間非常短的暫停。 本系列的第一篇文章重點(diǎn)介紹了引擎,運(yùn)行時(shí)和調(diào)用棧的概述。第二篇文章將深入V8的JavaScript引擎的內(nèi)部。我們還會(huì)提供一些關(guān)于如何編寫更好的JavaScript代碼的技巧。 概...
摘要:本文將會(huì)深入分析的引擎的內(nèi)部實(shí)現(xiàn)。該引擎使用在谷歌瀏覽器內(nèi)部。同其他現(xiàn)代引擎如或所做的一樣,通過實(shí)現(xiàn)即時(shí)編譯器在執(zhí)行時(shí)將代碼編譯成機(jī)器代碼。這可使正常執(zhí)行期間只發(fā)生相當(dāng)短的暫停。 原文 How JavaScript works: inside the V8 engine + 5 tips on how to write optimized code 幾周前我們開始了一個(gè)系列博文旨在深入...
摘要:第二篇文章將深入谷歌的引擎的內(nèi)部。引擎可以實(shí)現(xiàn)為標(biāo)準(zhǔn)解釋器,或者以某種形式將編譯為字節(jié)碼的即時(shí)編譯器。這個(gè)引擎是在谷歌中使用的,但是,與其他引擎不同的是也用于流行的。一種更復(fù)雜的優(yōu)化編譯器,生成高度優(yōu)化的代碼。不是唯一能夠做到的引擎。 本系列的 第一篇文章 主要介紹引擎、運(yùn)行時(shí)和調(diào)用堆棧。第二篇文章將深入谷歌 V8 的JavaScript引擎的內(nèi)部。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHu...
閱讀 1868·2021-09-22 15:45
閱讀 1652·2019-08-30 15:55
閱讀 1838·2019-08-29 11:16
閱讀 3312·2019-08-26 11:44
閱讀 714·2019-08-23 17:58
閱讀 2703·2019-08-23 12:25
閱讀 1637·2019-08-22 17:15
閱讀 3615·2019-08-22 16:09