国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

用 JavaScript 解釋 JavaScript 虛擬機-內聯緩存(inline caches)

mikyou / 1979人閱讀

摘要:用解釋虛擬機內聯緩存本文轉載自眾成翻譯譯者鏈接原文我知道如何實現用語言或者語言的子集來實現運行該語言虛擬機。有時候我們用了錯誤的抽象層次來解釋虛擬機的工作機制。這正是我們的內聯緩存功能所需要的。

用JavaScript解釋JavaScript虛擬機-內聯緩存(inline caches)

本文轉載自:眾成翻譯
譯者:LexHuang
鏈接:http://www.zcfy.cc/article/2959
原文:http://mrale.ph/blog/2012/06/03/explaining-js-vms-in-js-inline-caches.html

我知道如何實現用語言(或者語言的子集)來實現運行該語言虛擬機。如果我在學校或者有更多的時間我肯定會用JavaScript實現一個JavaScript虛擬機。實際上這并不會變成一個獨一無二的JavaScript項目,因為蒙特利爾大學的人所造的Tachyon已經在某種程度上達到了同樣的目的,但是我也有些我自己想要追求的點子。

我則有另一個和自循環虛擬機緊密相關的夢想。我想要幫助JavaScript開發者理解JS引擎的工作方式。我認為理解你正在使用的工具是我們職業生涯中最重要的。越多人不在把JS VM看作是將JavaScript源碼轉為0-1神秘的黑盒越好。

我應該說我不是一個人在追求如何解釋虛擬機的內部機制并且幫助人們編寫性能更好的代碼。全世界有許多人正在嘗試做同樣的事情。但是我認為又一個問題正在阻止知識有效地被開發者所吸收——我們正在用錯誤的形式來傳授我們的知識。我對此深感愧疚:

有時候我把我對V8的了解包裝成了很難消化的“做這個,別做那個”的教條化意見。這樣的問題在于它對于解釋起不到任何幫助并且很容易隨著關注人的減少而消失。

有時候我們用了錯誤的抽象層次來解釋虛擬機的工作機制。我喜歡一個想法:看見滿是匯編代碼的ppt演示可能會鼓勵人們去學習匯編并且學會之后會去讀ppt演示的內容。但我也害怕有時候這些ppt只會被人忽視和遺忘而對于實踐毫無用處。

我一直在思考這些問題很長時間了并且我認為用JavaScript來解釋JavaScript虛擬機是一個值得嘗試的事情。我在WebRebels 2012發表的演講“V8 Inside Out”追求的正是這一點[視頻][演示]并且在本文中我像回顧我一直在奧斯陸所談論的事情但是不同的是不會有任何音頻的干擾。(我認為我寫作的方式比我演講的方式更加嚴肅些 ?)。

用JavaScript來實現動態語言

想象你想要為了一個在語法上非常類似于JavaScript但是有著更簡單的對象模型的語言——用表來映射key到任意類型的值來代替JavaScript對象——而來用JavaScript實現其虛擬機。簡單起見,讓我們想象Lua, 既像JavaScript但作為一個語言又很不一樣。我最喜歡的“造出一個充滿點的數組然后去計算向量合”的例子看起來大致如下:

function MakePoint(x, y)
  local point = {}
  point.x = x
  point.y = y
  return point
end

function MakeArrayOfPoints(N)
  local array = {}
  local m = -1
  for i = 0, N do
    m = m * -1
    array[i] = MakePoint(m * i, m * -i)
  end
  array.n = N
  return array
end

function SumArrayOfPoints(array)
  local sum = MakePoint(0, 0)
  for i = 0, array.n do
    sum.x = sum.x + array[i].x
    sum.y = sum.y + array[i].y
  end
  return sum
end

function CheckResult(sum)
  local x = sum.x
  local y = sum.y
  if x ~= 50000 or y ~= -50000 then
    error("failed: x = " .. x .. ", y = " .. y)
  end
end

local N = 100000
local array = MakeArrayOfPoints(N)
local start_ms = os.clock() * 1000;
for i = 0, 5 do
  local sum = SumArrayOfPoints(array)
  CheckResult(sum)
end
local end_ms = os.clock() * 1000;
print(end_ms - start_ms)

注意我有一個至少檢查某些最終結果的微型基準測試的習慣。這有助于當有人發現我的革命性的jsperf測試用例只不過是我自己的bug時,讓我不會太尷尬。

如果你拿上面的例子放入一個Lua編譯器你會得到類似于下面的東西:

∮ lua points.lua
150.2

很好,但是對于了解虛擬機的工作過程起不到任何幫助。所以讓我們想想如果我們有用JavaScript編寫的類Lua虛擬機會長什么樣。“類”是因為我不想實現完全類似于Lua的語法,我更喜歡只關注于用表來實現對象這一點上。原生編譯器應該會將我們的代碼編譯成下面的JavaScript:

function MakePoint(x, y) {
  var point = new Table();
  STORE(point, "x", x);
  STORE(point, "y", y);
  return point;
}

function MakeArrayOfPoints(N) {
  var array = new Table();
  var m = -1;
  for (var i = 0; i <= N; i++) {
    m = m * -1;
    STORE(array, i, MakePoint(m * i, m * -i));
  }
  STORE(array, "n", N);
  return array;
}

function SumArrayOfPoints(array) {
  var sum = MakePoint(0, 0);
  for (var i = 0; i <= LOAD(array, "n"); i++) {
    STORE(sum, "x", LOAD(sum, "x") + LOAD(LOAD(array, i), "x"));
    STORE(sum, "y", LOAD(sum, "y") + LOAD(LOAD(array, i), "y"));
  }
  return sum;
}

function CheckResult(sum) {
  var x = LOAD(sum, "x");
  var y = LOAD(sum, "y");
  if (x !== 50000 || y !== -50000) {
    throw new Error("failed: x = " + x + ", y = " + y);
  }
}

var N = 100000;
var array = MakeArrayOfPoints(N);
var start = LOAD(os, "clock")() * 1000;
for (var i = 0; i <= 5; i++) {
  var sum = SumArrayOfPoints(array);
  CheckResult(sum);
}
var end = LOAD(os, "clock")() * 1000;
print(end - start);

但是如果你嘗試用d8(V8的獨立shell)去運行編譯后的代碼,它會很禮貌的拒絕:

∮ d8 points.js
points.js:9: ReferenceError: Table is not defined
  var array = new Table();
                  ^
ReferenceError: Table is not defined
    at MakeArrayOfPoints (points.js:9:19)
    at points.js:37:13

失敗的原因很簡單:我們還缺少負責實現對象模型和存取語法的運行時系統代碼。這可能看起來很明顯,但是我想要強調的是:虛擬機從外面看起來像是黑盒,在內部實際上是一系列盒子為了得到出最佳性能的相互協作。這些盒子是:編譯器、運行時例程、對象模型、垃圾回收等。幸運的是我們的語言和例子非常簡單所以我們的運行時系統僅僅多了幾行代碼:

function Table() {
  // Map from ES Harmony is a simple dictionary-style collection.
  this.map = new Map;
}

Table.prototype = {
  load: function (key) { return this.map.get(key); },
  store: function (key, value) { this.map.set(key, value); }
};

function CHECK_TABLE(t) {
  if (!(t instanceof Table)) {
    throw new Error("table expected");
  }
}

function LOAD(t, k) {
  CHECK_TABLE(t);
  return t.load(k);
}

function STORE(t, k, v) {
  CHECK_TABLE(t);
  t.store(k, v);
}

var os = new Table();

STORE(os, "clock", function () {
  return Date.now() / 1000;
});

注意到我用了ES6的Map而不是一般的JavaScript對象因為潛在的表可以使用任何鍵,而不僅是字符串形式的。

∮ d8 **--harmony** quasi-lua-runtime.js points.js
737

現在我們編譯后的代碼可以執行但是卻慢地令人失望,因為每一次讀和寫不得不跨越所有這些抽象層級后才能拿到值。讓我們通過所有JavaScript虛擬機都有的最基本的優化inline caching來嘗試減少這些開銷。即使是用Java實現的JS虛擬機最終也會使用它因為動態調用的本質是被暴露在字節碼層面的結構化的內聯緩存。Inline caching(在V8資源里通常簡寫為IC)實際上是一門近30年的非常古老的技術,最初用在Smalltalk虛擬機上。

好鴨子總是叫地一模一樣

內聯緩存(Inline caching)背后的思想非常簡單:創建一個高速路來繞過運行時系統來讀取對象的屬性:對傳入的對象及其屬性作出某種假設,然后通過一個低成本的方式驗證這個假設是否正確,如果正確就讀取上次緩存的結果。在充滿了動態類型和晚綁定以及其他古怪行為——比如eval——的語言里對一個對象作出合理的假設是非常困難的,所以我們退而求其次,讓我們的讀/寫操作能夠有學習能力:一旦它們看見某個對象它們就可以以某種方式來自適應,使得之后的讀取操作在遇到類似結構的對象時能夠更快地進行。在某種意義上,我們將要在讀/寫操作上緩存關于之前見過的對象的布局的相關知識——這也是內聯緩存這個名字的由來。內聯緩存可以被用在幾乎所有需要動態行為的操作上,只要你可以找到正確的高速路:算數操作、調用自由函數、方法調用等等。有些內聯緩存還能緩存不止一條快速通道,這些內聯緩存就變成了多態的。

如果我們開始思考如何應用內聯緩存到上面編譯后的代碼,答案就變得顯而易見了:我們需要改變我們的對象模型。我們不可能從一個map中進行快速讀取,因為我們總是要調用get方法。[如果我們能夠窺探map后的純哈希表,我們就可以通過緩存桶索引來讓內聯緩存替我們工作而不需要相處一個新的對象布局。]

探索隱藏結構

出于效率角度考慮,用作數據結構的表應該更類似于C結構:帶有固定偏移量的命名字段序列。這樣表就和數組類似:我們希望數字形式的屬性的存儲類似于數組。但是很顯然并不是所有表的鍵都是數字:鍵可以被設計成非字符串非數字或者包含太多字符串命名的屬性,并且隨著表的修改鍵也會隨之修改。不幸的是,我們不能做任何昂貴的類型推斷。取而代之我們必須找在程序運行期間的每一個表背后的結構,并且隨著程序的運行可以創建和修改它們。幸運的是,有一個眾所周知的技術 ?:_隱藏類(hidden classes)_。

隱藏類背后的思想可以歸結為以下兩點:

對于每個javascript對象,運行時系統都會將其合一個hidden class關聯起來。就像Java VM會關聯一個java.lang.Class的實例給每個對象一樣。

如果對象的布局改變了,則運行時就會 找到一個hidden class或者創建一個新的hidden class來匹配這個新對象布局并且連接到該對象上。

隱藏類有個非常重要的特性:它們運行虛擬機通過簡單比對緩存過的隱藏類來檢查關于某個對象布局的假設。這正是我們的內聯緩存功能所需要的。讓我們為我們的類-Lua運行時來實現一些簡單的隱藏類系統。每個隱藏類本質上是屬性描述符的集合,每個描述符要么是一個真正的屬性,要么是一個過渡(transition):從一個沒有該屬性的類指向一個有該屬性的類。

function Transition(klass) {
  this.klass = klass;
}

function Property(index) {
  this.index = index;
}

function Klass(kind) {
  // Classes are "fast" if they are C-struct like and "slow" is they are Map-like.
  this.kind = kind;
  this.descriptors = new Map;
  this.keys = [];
}

過渡之所以存在是為了讓多個對象之間能共享隱藏類:如果你有兩個對象共享了隱藏類并且你為它們同時增加了某些屬性,你不希望得到不同的隱藏類。

Klass.prototype = {
  // Create hidden class with a new property that does not exist on
  // the current hidden class.
  addProperty: function (key) {
    var klass = this.clone();
    klass.append(key);
    // Connect hidden classes with transition to enable sharing:
    //           this == add property key ==> klass
    this.descriptors.set(key, new Transition(klass));
    return klass;
  },

  hasProperty: function (key) {
    return this.descriptors.has(key);
  },

  getDescriptor: function (key) {
    return this.descriptors.get(key);
  },

  getIndex: function (key) {
    return this.getDescriptor(key).index;
  },

  // Create clone of this hidden class that has same properties
  // at same offsets (but does not have any transitions).
  clone: function () {
    var klass = new Klass(this.kind);
    klass.keys = this.keys.slice(0);
    for (var i = 0; i < this.keys.length; i++) {
      var key = this.keys[i];
      klass.descriptors.set(key, this.descriptors.get(key));
    }
    return klass;
  },

  // Add real property to descriptors.
  append: function (key) {
    this.keys.push(key);
    this.descriptors.set(key, new Property(this.keys.length - 1));
  }
};

現在我們可以讓我們的表變得更加靈活并且能允許它們自適應其自身地構造方式

var ROOT_KLASS = new Klass("fast");

function Table() {
  // All tables start from the fast empty root hidden class and form 
  // a single tree. In V8 hidden classes actually form a forest - 
  // there are multiple root classes, e.g. one for each constructor. 
  // This is partially due to the fact that hidden classes in V8 
  // encapsulate constructor specific information, e.g. prototype 
  // poiinter is actually stored in the hidden class and not in the 
  // object itself so classes with different prototypes must have 
  // different hidden classes even if they have the same structure.
  // However having multiple root classes also allows to evolve these
  // trees separately capturing class specific evolution independently.
  this.klass = ROOT_KLASS;
  this.properties = [];  // Array of named properties: "x","y",...
  this.elements = [];  // Array of indexed properties: 0, 1, ...
  // We will actually cheat a little bit and allow any int32 to go here,
  // we will also allow V8 to select appropriate representation for
  // the array"s backing store. There are too many details to cover in
  // a single blog post :-)
}

Table.prototype = {
  load: function (key) {
    if (this.klass.kind === "slow") {
      // Slow class => properties are represented as Map.
      return this.properties.get(key);
    }

    // This is fast table with indexed and named properties only.
    if (typeof key === "number" && (key | 0) === key) {  // Indexed property.
      return this.elements[key];
    } else if (typeof key === "string") {  // Named property.
      var idx = this.findPropertyForRead(key);
      return (idx >= 0) ? this.properties[idx] : void 0;
    }

    // There can be only string&number keys on fast table.
    return void 0;
  },

  store: function (key, value) {
    if (this.klass.kind === "slow") {
      // Slow class => properties are represented as Map.
      this.properties.set(key, value);
      return;
    }

    // This is fast table with indexed and named properties only.
    if (typeof key === "number" && (key | 0) === key) {  // Indexed property.
      this.elements[key] = value;
      return;
    } else if (typeof key === "string") {  // Named property.
      var index = this.findPropertyForWrite(key);
      if (index >= 0) {
        this.properties[index] = value;
        return;
      }
    }

    this.convertToSlow();
    this.store(key, value);
  },

  // Find property or add one if possible, returns property index
  // or -1 if we have too many properties and should switch to slow.
  findPropertyForWrite: function (key) {
    if (!this.klass.hasProperty(key)) {  // Try adding property if it does not exist.
      // To many properties! Achtung! Fast case kaput.
      if (this.klass.keys.length > 20) return -1;

      // Switch class to the one that has this property.
      this.klass = this.klass.addProperty(key);
      return this.klass.getIndex(key);
    }

    var desc = this.klass.getDescriptor(key);
    if (desc instanceof Transition) {
      // Property does not exist yet but we have a transition to the class that has it.
      this.klass = desc.klass;
      return this.klass.getIndex(key);
    }

    // Get index of existing property.
    return desc.index;
  },

  // Find property index if property exists, return -1 otherwise.
  findPropertyForRead: function (key) {
    if (!this.klass.hasProperty(key)) return -1;
    var desc = this.klass.getDescriptor(key);
    if (!(desc instanceof Property)) return -1;  // Here we are not interested in transitions.
    return desc.index;
  },

  // Copy all properties into the Map and switch to slow class.
  convertToSlow: function () {
    var map = new Map;
    for (var i = 0; i < this.klass.keys.length; i++) {
      var key = this.klass.keys[i];
      var val = this.properties[i];
      map.set(key, val);
    }

    Object.keys(this.elements).forEach(function (key) {
      var val = this.elements[key];
      map.set(key | 0, val);  // Funky JS, force key back to int32.
    }, this);

    this.properties = map;
    this.elements = null;
    this.klass = new Klass("slow");
  }
};

[我不打算一行一行地解釋上面的代碼,因為它已經是用JavaScript書寫的了;而不是C++ 或者 匯編...這正是使用JavaScript的意義所在。然而你可以通過評論或者郵件來詢問任何不理解的地方。]

既然我們已經在運行時系統里加入了隱藏類,使得我們能夠快速檢查對象的結構并且通過它們的索引來快速讀取屬性,我們只差實現內聯緩存了。這需要在編譯器和運行時系統增加一些新的功能(還記得我談論過虛擬機內不同成員之間的協作么?)。

打包生成后代碼

實現內聯緩存的途徑之一是將其分割成兩個部分:生成代碼里的可變調用點和可以被調用點調用的一系列存根(stubs,一小片生成的本地代碼)。非常重要的一點是:存根本身必須能從調用它們的調用點(或者運行時系統)中找到:存根只存放特定假設下的編譯后的快速路徑,如果這些假設對存根遇到的對象不適用,則存根可以初始化調用該存根的調用點的變動(打包,patching),使得該調用點能夠適應新的情況。我們的純JavaScript仍然包含兩個部分:

一個全局變量,每個ic都會使用一個全局變量來模擬可變調用指令;

并使用閉包來代替存根。

在本地代碼里, V8通過在棧上監聽返回地址來找到要打包的內聯緩存點。我們不能通過純JavaScript來達到這一點(arguments.caller的粒度不夠細)。所以我們將只會顯式地傳遞內聯緩存的id到內聯緩存的存根。通過內聯緩存優化后的代碼如下:

// Initially all ICs are in uninitialized state.
// They are not hitting the cache and always missing into runtime system.
var STORE$0 = NAMED_STORE_MISS;
var STORE$1 = NAMED_STORE_MISS;
var KEYED_STORE$2 = KEYED_STORE_MISS;
var STORE$3 = NAMED_STORE_MISS;
var LOAD$4 = NAMED_LOAD_MISS;
var STORE$5 = NAMED_STORE_MISS;
var LOAD$6 = NAMED_LOAD_MISS;
var LOAD$7 = NAMED_LOAD_MISS;
var KEYED_LOAD$8 = KEYED_LOAD_MISS;
var STORE$9 = NAMED_STORE_MISS;
var LOAD$10 = NAMED_LOAD_MISS;
var LOAD$11 = NAMED_LOAD_MISS;
var KEYED_LOAD$12 = KEYED_LOAD_MISS;
var LOAD$13 = NAMED_LOAD_MISS;
var LOAD$14 = NAMED_LOAD_MISS;

function MakePoint(x, y) {
  var point = new Table();
  STORE$0(point, "x", x, 0);  // The last number is IC"s id: STORE$0 ? id is 0
  STORE$1(point, "y", y, 1);
  return point;
}

function MakeArrayOfPoints(N) {
  var array = new Table();
  var m = -1;
  for (var i = 0; i <= N; i++) {
    m = m * -1;
    // Now we are also distinguishing between expressions x[p] and x.p.
    // The fist one is called keyed load/store and the second one is called
    // named load/store.
    // The main difference is that named load/stores use a fixed known
    // constant string key and thus can be specialized for a fixed property
    // offset.
    KEYED_STORE$2(array, i, MakePoint(m * i, m * -i), 2);
  }
  STORE$3(array, "n", N, 3);
  return array;
}

function SumArrayOfPoints(array) {
  var sum = MakePoint(0, 0);
  for (var i = 0; i <= LOAD$4(array, "n", 4); i++) {
    STORE$5(sum, "x", LOAD$6(sum, "x", 6) + LOAD$7(KEYED_LOAD$8(array, i, 8), "x", 7), 5);
    STORE$9(sum, "y", LOAD$10(sum, "y", 10) + LOAD$11(KEYED_LOAD$12(array, i, 12), "y", 11), 9);
  }
  return sum;
}

function CheckResults(sum) {
  var x = LOAD$13(sum, "x", 13);
  var y = LOAD$14(sum, "y", 14);
  if (x !== 50000 || y !== -50000) throw new Error("failed x: " + x + ", y:" + y);
}

上述的改變依舊是不言自明的:每一個屬性的讀/寫點都有屬于它們自己的、帶有id的內聯緩存。距離最終完成還剩一小步:實現未命中(MISS)存根和可以生存特定存根的“存根編譯器”:

function NAMED_LOAD_MISS(t, k, ic) {
  var v = LOAD(t, k);
  if (t.klass.kind === "fast") {
    // Create a load stub that is specialized for a fixed class and key k and
    // loads property from a fixed offset.
    var stub = CompileNamedLoadFastProperty(t.klass, k);
    PatchIC("LOAD", ic, stub);
  }
  return v;
}

function NAMED_STORE_MISS(t, k, v, ic) {
  var klass_before = t.klass;
  STORE(t, k, v);
  var klass_after = t.klass;
  if (klass_before.kind === "fast" &&
      klass_after.kind === "fast") {
    // Create a store stub that is specialized for a fixed transition between classes
    // and a fixed key k that stores property into a fixed offset and replaces
    // object"s hidden class if necessary.
    var stub = CompileNamedStoreFastProperty(klass_before, klass_after, k);
    PatchIC("STORE", ic, stub);
  }
}

function KEYED_LOAD_MISS(t, k, ic) {
  var v = LOAD(t, k);
  if (t.klass.kind === "fast" && (typeof k === "number" && (k | 0) === k)) {
    // Create a stub for the fast load from the elements array.
    // Does not actually depend on the class but could if we had more complicated
    // storage system.
    var stub = CompileKeyedLoadFastElement();
    PatchIC("KEYED_LOAD", ic, stub);
  }
  return v;
}

function KEYED_STORE_MISS(t, k, v, ic) {
  STORE(t, k, v);
  if (t.klass.kind === "fast" && (typeof k === "number" && (k | 0) === k)) {
    // Create a stub for the fast store into the elements array.
    // Does not actually depend on the class but could if we had more complicated
    // storage system.
    var stub = CompileKeyedStoreFastElement();
    PatchIC("KEYED_STORE", ic, stub);
  }
}

function PatchIC(kind, id, stub) {
  this[kind + "$" + id] = stub;  // non-strict JS funkiness: this is global object.
}

function CompileNamedLoadFastProperty(klass, key) {
  // Key is known to be constant (named load). Specialize index.
  var index = klass.getIndex(key);

  function KeyedLoadFastProperty(t, k, ic) {
    if (t.klass !== klass) {
      // Expected klass does not match. Can"t use cached index.
      // Fall through to the runtime system.
      return NAMED_LOAD_MISS(t, k, ic);
    }
    return t.properties[index];  // Veni. Vidi. Vici.
  }

  return KeyedLoadFastProperty;
}

function CompileNamedStoreFastProperty(klass_before, klass_after, key) {
  // Key is known to be constant (named load). Specialize index.
  var index = klass_after.getIndex(key);

  if (klass_before !== klass_after) {
    // Transition happens during the store.
    // Compile stub that updates hidden class.
    return function (t, k, v, ic) {
      if (t.klass !== klass_before) {
        // Expected klass does not match. Can"t use cached index.
        // Fall through to the runtime system.
        return NAMED_STORE_MISS(t, k, v, ic);
      }
      t.properties[index] = v;  // Fast store.
      t.klass = klass_after;  // T-t-t-transition!
    }
  } else {
    // Write to an existing property. No transition.
    return function (t, k, v, ic) {
      if (t.klass !== klass_before) {
        // Expected klass does not match. Can"t use cached index.
        // Fall through to the runtime system.
        return NAMED_STORE_MISS(t, k, v, ic);
      }
      t.properties[index] = v;  // Fast store.
    }
  }
}

function CompileKeyedLoadFastElement() {
  function KeyedLoadFastElement(t, k, ic) {
    if (t.klass.kind !== "fast" || !(typeof k === "number" && (k | 0) === k)) {
      // If table is slow or key is not a number we can"t use fast-path.
      // Fall through to the runtime system, it can handle everything.
      return KEYED_LOAD_MISS(t, k, ic);
    }
    return t.elements[k];
  }

  return KeyedLoadFastElement;
}

function CompileKeyedStoreFastElement() {
  function KeyedStoreFastElement(t, k, v, ic) {
    if (t.klass.kind !== "fast" || !(typeof k === "number" && (k | 0) === k)) {
      // If table is slow or key is not a number we can"t use fast-path.
      // Fall through to the runtime system, it can handle everything.
      return KEYED_STORE_MISS(t, k, v, ic);
    }
    t.elements[k] = v;
  }

  return KeyedStoreFastElement;
}

代碼很長(以及注釋),但是配合上面所有解釋應該不難理解:內聯緩存負責觀察而存根編譯器/工程負責生產自適應和特化后的存根[有心的讀者可能注意到了我本可以初始化所有鍵控的存儲內聯緩存(keyed store ICs),用一開始的快速讀取或者當它進入快速狀態后就一直保持住]。

如果我們不管上面所有代碼而回到我們的“基準測試”,我們會得到非常令人滿意的結果:

∮ d8 --harmony quasi-lua-runtime-ic.js points-ic.js
117

這要比我們一開始的天真嘗試提升了6倍!

關于JavaScript虛擬機優化永遠也不會有結論

希望你在閱讀這一部分的時候已經看完了之前所有內容...我嘗試從不同的角度,JavaScript開發者的角度,來看某些驅動當今JavaScript引擎的點子。所寫的代碼越長,我越有一種盲人摸象的感覺。下面的事實只是為了給你一種望向深淵的感覺:V8有10種描述符,5種元素類型(+9外部元素類型),ic.cc里包含了幾乎所有內聯緩存狀態選擇的邏輯多達2500行,并且V8的內聯緩存的狀態不止2個(它們是uninitialized, premonomorphic, monomorphic, polymorphic, generic states,更別提用于鍵控讀/寫的內聯緩存的特殊的狀態或者是算數內斂緩存的完全不同的狀態層級),ia32-specific手寫的內聯緩存存根多達5000行代碼,等等。這些數字只會隨著時間的流逝和V8為了識別和適應越來越多的對象布局的學習而增長。而且我甚至都還沒談到對象模型本身(objects.cc 13k行代碼),或者垃圾回收,或者優化編譯器。

話雖如此,在可預見的未來內,我確信基礎將不會改變,如果變了肯定會引發一場你一定會注意到的巨大的爆炸!因此我認為這次嘗試用JavaScript去理解基礎的練習是非常非常非常重要的。

我希望明天或者幾周之后你會停下來并且大喊“我找到了!”并且告訴你的為什么特定情況下在一個地方為一個對象增加屬性會影響其余很遠的接觸這些對象的熱回路的性能。_你知道的,因為隱藏類變了!_

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/84990.html

相關文章

  • V8 中的 Fast 屬性

    摘要:這些是中可用的最快屬性。通常來說我們將線性屬性存儲中存儲的屬性稱為。因此也支持所謂的屬性。整數索引屬性的處理和命名屬性的復雜性相同。 本文為譯文,原文地址:http://v8project.blogspot.com...,作者,@Camillo Bruni ,V8 JavaScript Engine Team Blog 在這篇博客中,我們想解釋 V8 如何在內部處理 JavaScrip...

    Andrman 評論0 收藏0
  • JavaScript如何工作:V8引擎深入探究 + 優化代碼的5個技巧(譯文)

    摘要:引擎可以是一個標準的解釋器,也可以是一個將編譯成某種形式的字節碼的即時編譯器。和其他引擎最主要的差別在于,不會生成任何字節碼或是中間代碼。不使用中間字節碼的表示方式,就沒有必要用解釋器了。 原文地址:https://blog.sessionstack.com... showImg(https://segmentfault.com/img/bVVwZ8?w=395&h=395); 數周之...

    William_Sang 評論0 收藏0
  • javascript運行環境和消息隊列

    摘要:字節碼不能直接運行,而是運行在一個虛擬機之上,一般也把虛擬機稱為引擎。這些事件排成隊列,等候進入主線程。執行至完成每一個消息執行完成后,其它消息才會被執行。零延遲零延遲并不是意味著回調會立即執行。 JavaScript虛擬機 JavaScript是一種解釋型語言,也就是說,它不需要編譯,可以由解釋器實時運行。這樣的好處是運行和修改都比較方便,刷新頁面就可以重新解釋;缺點是每次運行都要調...

    cppowboy 評論0 收藏0
  • 使 D8 分析 javascript 如何被 V8 引擎優化的

    摘要:負責找出經常被調用的代碼,做內聯緩存優化,后面的信息進一步說明了這個情況。我們再使用參數看看引擎如何去優化。如果使用,直接運行輸出的是的命令行參數,如果想查看的,需要使用。后面章節會介紹的命令行參數以及最有意思的。 在上一篇文章中我們講了如何使用 GN 編譯 V8 源碼,文章最后編譯完成的可執行文件并不是 V8,而是 D8。這篇我們講一下如何使用 D8 調試 javascript 代碼...

    Airmusic 評論0 收藏0
  • 頁面加速優化

    摘要:延遲加載當我們調用外部的時候,使用事件在頁面內部被加載前,外部將不被加載腳本調用外部文件拷貝以上代碼。代碼代碼片段組合外部工具列表頁面加速優化頁面請求工具工具大全擴展閱讀方面的設置 內聯 CSS 優點 使用內聯 CSS 可以減少瀏覽器去服務端去下載 CSS 文件 關鍵 CSS 內聯到 HTML 文件中 缺點 CSS 文件沒法被緩存 注意:該方法只適用于很小的 CSS...

    Lin_YT 評論0 收藏0

發表評論

0條評論

mikyou

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<