為V8優化JavaScript

V8可以讓JavaScript加速350倍,所以我們有很多優化的空間,在這之前,我們必須了解V8優化JavaScript的原理,然后寫出針對V8的代碼。

下面會使用"Be prepared"這個詞語,單詞的意思是:

  • 理解V8優化原理
  • 寫出深思熟慮的JavaScript代碼
  • 使用工具測試性能,幫助改進

隱藏類

變量類型對生成高速優化的代碼非常有幫助,但是JavaScript確實若類型的。如何才能讓JavaScript跑的像C++一樣快呢?答案就hidden classes

Hidden Classes讓JavaScript更快

  • V8在內部為創建隱藏類
  • 具有相同隱藏類的對象可以使用相同的優化代碼

如果不理解上面的原理,可以查看V8設計原理或者ppt

tips:使用構造函數初始化數據

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

當V8解析p2.z = 55時,p1,p2使用了不同的隱藏類了,就意味著要創建一個新的隱藏類,cache也要重建,所以盡量不要這樣。如果你沒有用構造函數,請保證對象賦屬性的順序是一樣的。

高效的描述值

Be Prepared - Numbers

我們看到下圖中,V8使用一個標簽來表示不同的對象,很明顯對于數字,我們使用能用31位有符號整數是效率最高的。


Tagged Values

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有兩種處理數組的方式

  • Fast Elements: 線性存貯,連續的buffer,性能好
  • Dictionary Elements: hash table storage otherwise

避免性能陷阱

  • 使用從0開始連續的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
  • 不要預先建立太大的數組(e.g. > 64K elements),JavaScript不需要像C語言指定數組大小
    這樣會變成稀疏數組,也會使用字典模式創建


    不同的數組模式
  • 不要刪除數據,特別是數值型的數組
    這樣會導致兩種模式的切換,都會很費時

  • 不要使用未初始化的數組,或者被刪除的元素

a = new Array();
for (var b = 0; b < 10; b++) {
  a[0] |= b;  // Oh no! 這里a[0]是undefined,v8會做轉換,結果是對的,但是更費時間
}
//vs.
a = new Array();
a[0] = 0;
for (var b = 0; b < 10; b++) {
  a[0] |= b;  // Much better! 2x faster.
}
  • 不要導致數組boxing and unboxing
    看看下圖,既有double,又有其他類型,下面會導致hidden class的兩次轉變和三次申請空間。如果不理解可以看視頻。


    Paste_Image.png
var a = [77, 88, 0.5, true]; 這樣讓解析器一次知道所有信息會更好

總結一下使用數組需要注意的地方:

  1. 使用[]初始化數組
  2. 小數組可以先指定大小 (<64k) ,因為會使用快速模式


    小數組

    指定大小
  3. 不要在數字數組里面使用非數字,數值型的性能已經優化過,包括double
  4. 如果沒有使用數組字面量初始化數組,注意不必要的轉換發生

Be Prepared - Full Compiler

V8有兩個編譯器。你沒聽錯,是編譯器,JavaScript是動態語言,一般的動態語言都由解析器解析執行,但是V8可以直接編譯成可執行代碼。

  • "Full" compiler 為所有JavaScript生成可執行代碼
  • Optimizing compiler 為大多數JavaScript代碼生成更優化的代碼

"Full" Compiler立刻運行代碼

  • 生成好的但不是最好的JIT代碼,但是支持所有的JavaScript功能
  • 編譯期間并不假定類型信息,并期待類型在運行時變化
  • 運行的時候獲取類型并使用Inline Caches (or ICs)去加速執行
    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

生成的匯編代碼使用了IC

IC的目的是加速處理類型信息,它為JavaScript操作存貯類型相關的代碼,當代碼運行的時候,它驗證所假定的類型信息,然后使用IC去處理。所以,能處理多種類型的操作性能要查一些。

優化tips:

單一的操作比多樣的操作好
Monomorphic use of operations is preferred over polymorphic operations

function add(x, y) {
  return x + y;
}

add(1, 2);      // + in add is monomorphic(所有的操作都是數值類型的話)
add("a", "b");  // + in add becomes polymorphic

Optimizing compiler

優化編譯實際上使用inline技術,還記得在C++中的inline嗎,是一個意思。短小的函數,并且經常調用的函數,會被編譯器優化成inline。一般單一類型的函數和構造函數會被inline。

我們看一下代碼(**inline可以避免跳轉
**):


inline會避免跳轉

深度優化后更快了

一些有用的命令

d8 --trace-opt primes.js //log names of optimized functions to stdout
d8 --trace-bailout primes.js //找到被try catch包住不能優化的函數
d8 --trace-deopt primes.js //v8必須取消優化的代碼,找到以后可以修改
給chrome加啟動參數
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \--js-flags="--trace-opt --trace-deopt --trace-bailout"

prime.js是一個測試獲得質數的函數,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

d8

這些命令跑出來的結果還看不太懂,等以后仔細研究在來分享
命令結果案例

本文翻譯自這個ppt,可以觀看youtube演講
這篇文章也很好Performance Tips for JavaScript in V8

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容