The transmission of evaluative or corrective information about an action, event, or process to the original or controlling source; also : the information so transmitted
source: Merriam-Webster
Google proprietary
Google proprietary
Google proprietary
Google proprietary
Run for a while
Gather feedback
IC Slot | Type | Value |
---|---|---|
1 | LOAD | MONO |
2 | CALL | UNINIT |
... | ... | ... |
Feedback Vector
Google proprietary
Slot | Type | Value |
---|---|---|
1 | LOAD | MONO |
2 | CALL | UNINIT |
... | ... | ... |
Feedback Vector
Google proprietary
Slot | Type | Value |
---|---|---|
1 | LOAD | MONO |
2 | CALL | UNINIT |
... | ... | ... |
Google proprietary
Google proprietary
Google proprietary
Map M
x: 1
let o = { x: 1 };
o.y = 2;
Google proprietary
Map M
x: 1
Map N
x: 1
y: 2
let o = { x: 1 };
o.y = 2;
Google proprietary
Map M
x: 1
Map N
x: 1
y: 2
let o = { x: 1 };
o.y = 2;
Google proprietary
Google proprietary
IC Slot | IC Type | Value |
---|---|---|
1 | LOAD | MONO |
2 | CALL | UNINIT |
... | ... | ... |
Google proprietary
Every function has a Feedback Vector. It's just an array that holds state for each IC.
function processLogFile(fileName) {
this.collectEntries = true;
this.lastLogFileName_ = fileName;
var line;
while (line = readline()) {
this.processLogLine(line);
}
print();
print("Load: " + this.LoadIC);
print("Store: " + this.StoreIC);
...
}
IC Slot | IC Type | State |
---|---|---|
... | ... | ... |
10 | LOAD | MONO(M) |
11 | LOAD | UNINITIALIZED |
Google proprietary
An Inline Cache is also a state machine.
Uninitialized
Premonomorphic
Monomorphic
Polymorphic
Megamorphic
Slow
Google proprietary
function load(a) { return a.key; }
// pseudo-code for the LOAD IC:
if (vector[slot].state == MONO) {
if (a.map == vector[slot].map) {
return valueAtOffset(a, vector[slot].offset);
} else {
// Call into the Runtime for instructions.
// Update IC state.
}
} else {
..
}
Slot | Type | State | Map | Offset |
---|---|---|---|---|
0 | LOAD_IC | MONO | m | 0x8 |
Feedback Vector:
Google proprietary
Google proprietary
Google proprietary
Let's look at a simple property load.
function load(a) {
return a.key;
}
var o = { key: "usb" };
var o1 = { key: "port" };
for (var i = 0; i < 100000; i++) {
load(o);
load(o1);
}
Google proprietary
Here is the AST from parsing:
function load(a) {
return a.key;
}
var o = { key: "usb" };
var o1 = { key: "port" };
for (var i = 0; i < 100000; i++) {
load(o);
load(o1);
}
// Run with --print-ast
FUNC
. NAME "load"
. PARAMS
. . VAR "a"
. RETURN
. . PROPERTY Slot(0) at 29
. . . VAR PROXY parameter[0] "a"
. . . NAME key
Google proprietary
Here is the AST from parsing:
function load(a) {
return a.key;
}
var o = { key: "usb" };
var o1 = { key: "port" };
for (var i = 0; i < 100000; i++) {
load(o);
load(o1);
}
// Run with --print-ast
FUNC
. NAME "load"
. PARAMS
. . VAR "a"
. RETURN
. . PROPERTY Slot(0) at 29
. . . VAR PROXY parameter[0] "a"
. . . NAME key
Here is the feedback vector specification:
Slot | Type |
---|---|
0 | LOAD_IC |
Google proprietary
Here is the bytecode:
function load(a) {
return a.key;
}
var o = { key: "usb" };
var o1 = { key: "port" };
for (var i = 0; i < 100000; i++) {
load(o);
load(o1);
}
Here is the feedback vector specification:
// Run with --print-bytecode
"load" -- Parameter count 2
StackCheck
Nop
LdaNamedProperty a0, [0], [3] // "key"
Return
Slot | Type |
---|---|
0 | LOAD_IC |
Google proprietary
function load(a) {
return a.key;
}
var o = { key: "usb" };
var o1 = { key: "port" };
for (var i = 0; i < 100000; i++) {
load(o);
load(o1);
}
Here is the feedback vector:
Slot | Type | Value |
---|---|---|
0 | LOAD_IC | UNINIT |
Executing...
Google proprietary
function load(a) {
return a.key;
}
var o = { key: "usb" };
var o1 = { key: "port" };
for (var i = 0; i < 100000; i++) {
load(o);
load(o1);
}
We execute the LoadIC for "a.key"...
Slot | Type | Value |
---|---|---|
0 | LOAD_IC | UNINIT |
Google proprietary
function load(a) {
return a.key;
}
var o = { key: "usb" };
var o1 = { key: "port" };
for (var i = 0; i < 100000; i++) {
load(o);
load(o1);
}
Return the answer & remember the Map.
Slot | Type | Value |
---|---|---|
0 | LOAD_IC | MONO(o.map) |
Google proprietary
function load(a) {
return a.key;
}
var o = { key: "usb" };
var o1 = { key: "port" };
for (var i = 0; i < 100000; i++) {
load(o);
load(o1);
}
Now with a different object...
Slot | Type | Value |
---|---|---|
0 | LOAD_IC | MONO(o.map) |
Google proprietary
function load(a) {
return a.key;
}
var o = { key: "usb" };
var o1 = { key: "port" };
for (var i = 0; i < 100000; i++) {
load(o);
load(o1);
}
Is the map the same?
o1.map == o.map?
Slot | Type | Value |
---|---|---|
0 | LOAD_IC | MONO(o.map) |
Google proprietary
function load(a) {
return a.key;
}
var o = { key: "usb" };
var o1 = { key: "port" };
for (var i = 0; i < 100000; i++) {
load(o);
load(o1);
}
We remain monomorphic.
Yes
Slot | Type | Value |
---|---|---|
0 | LOAD_IC | MONO(o.map) |
Google proprietary
function load(a) {
return a.key;
}
var o = { key: "usb" };
var o1 = { key: "port" };
for (var i = 0; i < 100000; i++) {
load(o);
load(o1);
}
The Runtime Profiler asks:
// --trace-opt output
[marking <JSFunction load> for
optimized recompilation,
reason: small function,
ICs with typeinfo: 1/1 (100%),
generic ICs: 0/1 (0%)]
Slot | Type | Value |
---|---|---|
0 | LOAD_IC | MONO(o.map) |
(Run with --nouse-osr to ensure we optimize load(), and not the whole script)
// load(a) - Turbofanned
push ebp // Build frame
mov ebp,esp //
push esi //
push edi //
mov eax,[ebp+0x8] // eax = a
test al,0x1 // is a an object?
jz DEOPT_0 // If not, deoptimize
mov ecx,[eax-0x1] // ecx = a.map
mov edx,0x37e0b4ad // edx = o.map
cmp ecx, edx
jnz DEOPT_1 // if not same, deopt.
mov eax,[eax+0xb] // return a.key!
mov esp,ebp // Tear down frame
pop ebp //
ret 0x8 // return for realz
...
DEOPT_0: call 0x52d06000 // Goodbye...
DEOPT_1: call 0x52d0600a // Also goodbye
// load(a) - Turbofanned
push ebp // Build frame
mov ebp,esp //
push esi //
push edi //
mov eax,[ebp+0x8] // eax = a
test al,0x1 // is a an object?
jz DEOPT_0 // If not, deoptimize
mov ecx,[eax-0x1] // ecx = a.map
mov edx,0x37e0b4ad // edx = o.map
cmp ecx, edx
jnz DEOPT_1 // if not same, deopt.
mov eax,[eax+0xb] // return a.key!
mov esp,ebp // Tear down frame
pop ebp //
ret 0x8 // return for realz
...
DEOPT_0: call 0x52d06000 // Goodbye...
DEOPT_1: call 0x52d0600a // Also goodbye
Boilerplate
// load(a) - Turbofanned
push ebp // Build frame
mov ebp,esp //
push esi //
push edi //
mov eax,[ebp+0x8] // eax = a
test al,0x1 // is a an object?
jz DEOPT_0 // If not, deoptimize
mov ecx,[eax-0x1] // ecx = a.map
mov edx,0x37e0b4ad // edx = o.map
cmp ecx, edx
jnz DEOPT_1 // if not same, deopt.
mov eax,[eax+0xb] // return a.key!
mov esp,ebp // Tear down frame
pop ebp //
ret 0x8 // return for realz
...
DEOPT_0: call 0x52d06000 // Goodbye...
DEOPT_1: call 0x52d0600a // Also goodbye
Boilerplate
Object check
// load(a) - Turbofanned
push ebp // Build frame
mov ebp,esp //
push esi //
push edi //
mov eax,[ebp+0x8] // eax = a
test al,0x1 // is a an object?
jz DEOPT_0 // If not, deoptimize
mov ecx,[eax-0x1] // ecx = a.map
mov edx,0x37e0b4ad // edx = o.map
cmp ecx, edx
jnz DEOPT_1 // if not same, deopt.
mov eax,[eax+0xb] // return a.key!
mov esp,ebp // Tear down frame
pop ebp //
ret 0x8 // return for realz
...
DEOPT_0: call 0x52d06000 // Goodbye...
DEOPT_1: call 0x52d0600a // Also goodbye
Boilerplate
Object check
Map check
// load(a) - Turbofanned
push ebp // Build frame
mov ebp,esp //
push esi //
push edi //
mov eax,[ebp+0x8] // eax = a
test al,0x1 // is a an object?
jz DEOPT_0 // If not, deoptimize
mov ecx,[eax-0x1] // ecx = a.map
mov edx,0x37e0b4ad // edx = o.map
cmp ecx, edx
jnz DEOPT_1 // if not same, deopt.
mov eax,[eax+0xb] // return a.key!
mov esp,ebp // Tear down frame
pop ebp //
ret 0x8 // return for realz
...
DEOPT_0: call 0x52d06000 // Goodbye...
DEOPT_1: call 0x52d0600a // Also goodbye
Boilerplate
Object check
Map check
The actual load!
Google proprietary
"Bike Social" by Marc van Woudenberg (license)
Google proprietary
The more references there are to "a" downwind of the feedback, the greater the benefit of knowing the object's class.
Downwind includes inlined functions.
Google proprietary
// TurboFan emits something like this:
if (a.map == 0x43501231) { // Maps embedded in code.
return loadAtOffset(a, 0xc); // No Feedback Vector.
} else if (a.map == 0x99503211) {
return loadAtOffset(a, 0x10);
} else {
DEOPTIMIZE(); // Oh noes!
}
Google proprietary
Google proprietary
You lose touch with the Feedback Vector, and have nowhere to put the new map after deoptimization.
Eventually V8 stops reoptimizing.
Google proprietary
Google proprietary
Slot | Type | Value |
---|---|---|
0 | LOAD_IC | MONO(o.map) |
// load(a) - Turbofanned
...
mov ecx,[eax-0x1] // ecx = a.map
mov edx,0x37e0b4ad // edx = o.map
cmp ecx, edx
jnz DEOPT_1 // if not same, deopt
mov eax,[eax+0xb] // return a.key
...
Feedback Vector not being used
function load(a) {
return a.key;
}
var o = { key: "usb" };
var o1 = { key: "port" };
for (var i = 0; i < 100000; i++) {
load(o);
load(o1);
}
load({ key: "usb", name: "francis" });
Google proprietary
function load(a) {
return a.key;
}
var o = { key: "usb" };
var o1 = { key: "port" };
for (var i = 0; i < 100000; i++) {
load(o);
load(o1);
}
load({ key: "usb", name: "francis" });
Slot | Type | Value |
---|---|---|
0 | LOAD_IC | MONO(o.map) |
// load(a) - Turbofanned
...
mov ecx,[eax-0x1] // ecx = a.map
mov edx,0x37e0b4ad // edx = o.map
cmp ecx, edx
jnz DEOPT_1 // if not same, deopt
mov eax,[eax+0xb] // return a.key
...
Google proprietary
function load(a) {
return a.key;
}
var o = { key: "usb" };
var o1 = { key: "port" };
for (var i = 0; i < 100000; i++) {
load(o);
load(o1);
}
load({ key: "usb", name: "francis" });
Slot | Type | Value |
---|---|---|
0 | LOAD_IC | POLY(o.map, new_map) |
Feedback is updated
Google proprietary
IC Slot | IC Type | Value |
---|---|---|
1 | LOAD | MONO |
2 | CALL | UNINIT |
... | ... | ... |
Google proprietary