V8可以讓JavaScript加速350倍,所以我們有很多優(yōu)化的空間幸逆,在這之前纵隔,我們必須了解V8優(yōu)化JavaScript的原理,然后寫出針對V8的代碼嘹承。
下面會使用"Be prepared"這個詞語窗价,單詞的意思是:
- 理解V8優(yōu)化原理
- 寫出深思熟慮的JavaScript代碼
- 使用工具測試性能,幫助改進(jìn)
隱藏類
變量類型對生成高速優(yōu)化的代碼非常有幫助叹卷,但是JavaScript確實若類型的撼港。如何才能讓JavaScript跑的像C++一樣快呢?答案就hidden classes
Hidden Classes讓JavaScript更快
- V8在內(nèi)部為創(chuàng)建隱藏類
- 具有相同隱藏類的對象可以使用相同的優(yōu)化代碼
如果不理解上面的原理骤竹,可以查看V8設(shè)計原理或者ppt
tips:使用構(gòu)造函數(shù)初始化數(shù)據(jù)
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(11, 22);
var p2 = new Point(33, 44);
p2.z = 55// warning! p1 and p2 now have// different hidden classes
當(dāng)V8解析p2.z = 55
時帝牡,p1,p2使用了不同的隱藏類了,就意味著要創(chuàng)建一個新的隱藏類蒙揣,cache也要重建靶溜,所以盡量不要這樣。如果你沒有用構(gòu)造函數(shù)懒震,請保證對象賦屬性的順序是一樣的罩息。
高效的描述值
Be Prepared - Numbers
我們看到下圖中,V8使用一個標(biāo)簽來表示不同的對象挎狸,很明顯對于數(shù)字扣汪,我們使用能用31位有符號整數(shù)是效率最高的断楷。
Prefer numeric values that can be represented as 31-bit signed integers
var i = 42; // this is a 31-bit signed integer
var j = 4.2; // this is a double-precision floating point number
Be Prepared - Arrays
V8有兩種處理數(shù)組的方式
- Fast Elements: 線性存貯锨匆,連續(xù)的buffer,性能好
- Dictionary Elements: hash table storage otherwise
避免性能陷阱
- 使用從0開始連續(xù)的key
下面明顯是字典形式,性能不如Fast Elements
模式
var person = [];
person["firstName"] = "John";
person["lastName"] = "Doe";
person["age"] = 46;
var x = person.length; // person.length will return 0
var y = person[0]; // person[0] will return undefined
-
不要預(yù)先建立太大的數(shù)組(e.g. > 64K elements)恐锣,JavaScript不需要像C語言指定數(shù)組大小
這樣會變成稀疏數(shù)組茅主,也會使用字典模式創(chuàng)建
不同的數(shù)組模式 不要刪除數(shù)據(jù),特別是數(shù)值型的數(shù)組
這樣會導(dǎo)致兩種模式的切換土榴,都會很費時不要使用未初始化的數(shù)組诀姚,或者被刪除的元素
a = new Array();
for (var b = 0; b < 10; b++) {
a[0] |= b; // Oh no! 這里a[0]是undefined,v8會做轉(zhuǎn)換玷禽,結(jié)果是對的赫段,但是更費時間
}
//vs.
a = new Array();
a[0] = 0;
for (var b = 0; b < 10; b++) {
a[0] |= b; // Much better! 2x faster.
}
-
不要導(dǎo)致數(shù)組boxing and unboxing
看看下圖,既有double矢赁,又有其他類型糯笙,下面會導(dǎo)致hidden class的兩次轉(zhuǎn)變和三次申請空間。如果不理解可以看視頻撩银。
Paste_Image.png
var a = [77, 88, 0.5, true]; 這樣讓解析器一次知道所有信息會更好
總結(jié)一下使用數(shù)組需要注意的地方:
- 使用
[]
初始化數(shù)組 -
小數(shù)組可以先指定大小 (<64k) 给涕,因為會使用快速模式
小數(shù)組
指定大小 - 不要在數(shù)字?jǐn)?shù)組里面使用非數(shù)字,數(shù)值型的性能已經(jīng)優(yōu)化過额获,包括double
- 如果沒有使用數(shù)組字面量初始化數(shù)組够庙,注意不必要的轉(zhuǎn)換發(fā)生
Be Prepared - Full Compiler
V8有兩個編譯器。你沒聽錯抄邀,是編譯器耘眨,JavaScript是動態(tài)語言,一般的動態(tài)語言都由解析器解析執(zhí)行境肾,但是V8可以直接編譯成可執(zhí)行代碼毅桃。
- "Full" compiler 為所有JavaScript生成可執(zhí)行代碼
- Optimizing compiler 為大多數(shù)JavaScript代碼生成更優(yōu)化的代碼
"Full" Compiler立刻運行代碼
- 生成好的但不是最好的JIT代碼,但是支持所有的JavaScript功能
- 編譯期間并不假定類型信息准夷,并期待類型在運行時變化
- 運行的時候獲取類型并使用Inline Caches (or ICs)去加速執(zhí)行
Paste_Image.png
Full Compiler Example
this.isPrimeDivisible = function(candidate) {
for (var i = 1; i <= this.prime_count; ++i) {
if (candidate % this.primes[i] == 0) return true;
}
return false;
}
candidate % this.primes[i]
會編譯成:
IC的目的是加速處理類型信息钥飞,它為JavaScript操作存貯類型相關(guān)的代碼,當(dāng)代碼運行的時候衫嵌,它驗證所假定的類型信息读宙,然后使用IC去處理。所以楔绞,能處理多種類型的操作性能要查一些结闸。
優(yōu)化tips:
單一的操作比多樣的操作好
Monomorphic use of operations is preferred over polymorphic operations
function add(x, y) {
return x + y;
}
add(1, 2); // + in add is monomorphic(所有的操作都是數(shù)值類型的話)
add("a", "b"); // + in add becomes polymorphic
Optimizing compiler
優(yōu)化編譯實際上使用inline技術(shù),還記得在C++中的inline嗎酒朵,是一個意思桦锄。短小的函數(shù),并且經(jīng)常調(diào)用的函數(shù)蔫耽,會被編譯器優(yōu)化成inline结耀。一般單一類型的函數(shù)和構(gòu)造函數(shù)會被inline。
我們看一下代碼(**inline可以避免跳轉(zhuǎn)
**):
一些有用的命令
d8 --trace-opt primes.js //log names of optimized functions to stdout
d8 --trace-bailout primes.js //找到被try catch包住不能優(yōu)化的函數(shù)
d8 --trace-deopt primes.js //v8必須取消優(yōu)化的代碼,找到以后可以修改
給chrome加啟動參數(shù)
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \--js-flags="--trace-opt --trace-deopt --trace-bailout"
prime.js是一個測試獲得質(zhì)數(shù)的函數(shù)图甜,ppt的作者用來測試性能用的碍粥。
function Primes() {
this.prime_count = 0;
this.primes = new Array(25000);
this.getPrimeCount = function() { return this.prime_count; }
this.getPrime = function(i) { return this.primes[i]; }
this.addPrime = function(i) {
this.primes[this.prime_count++] = i;
}
this.isPrimeDivisible = function(candidate) {
for (var i = 1; i <= this.prime_count; ++i) {
if ((candidate % this.primes[i]) == 0) return true;
}
return false;
}
};
function main() {
p = new Primes();
var c = 1;
while (p.getPrimeCount() < 25000) {
if (!p.isPrimeDivisible(c)) {
p.addPrime(c);
}
c++;
}
print(p.getPrime(p.getPrimeCount()-1));
}
main();
你需要編譯v8,獲得d8命令行黑毅,在windows在編譯可以參考這篇文章使用visual studio編譯v8
這些命令跑出來的結(jié)果還看不太懂嚼摩,等以后仔細(xì)研究在來分享
本文翻譯自這個ppt,可以觀看youtube演講
這篇文章也很好Performance Tips for JavaScript in V8