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