"Always initialize object members in the same order" 对于 V8 优化意味着什么?
What does "Always initialize object members in the same order" mean in regards to V8 optimizations?
阅读此 old article 中的 V8(微)优化
并且有一句话:
Always initialize object members in the same order
问题:在下面的示例中,总是以相同的顺序初始化对象成员是什么意思?
function Point(x, y) {
this.x = x; // this.x = x line first because x is the first arg
this.y = y; // this.y = y line second because y is the second arg
}
var p1 = new Point(11, 22);
var p2 = new Point(33, 44);
// At this point, p1 and p2 have a shared hidden class
p2.z = 55;
// warning! p1 and p2 now have different hidden classes!
更长的引用:
JavaScript has limited compile-time type information: types can be changed at runtime, so it's natural to expect that it is expensive to reason about JS types at compile time. This might lead you to question how JavaScript performance could ever get anywhere close to C++. However, V8 has hidden types created internally for objects at runtime; objects with the same hidden class can then use the same optimized generated code.
Until the object instance p2 has additional member ".z" added, p1 and p2 internally have the same hidden class - so V8 can generate a single version of optimized assembly for JavaScript code that manipulates either p1 or p2. The more you can avoid causing the hidden classes to diverge, the better performance you'll obtain.
Therefore:
Initialize all object members in constructor functions (so the instances don't change type later)
Always initialize object members in the same order
注意:我在C++中发现了类似的问题,但我真的看不懂Why should I initialize member variables in the order they're declared in?
这里是 V8 开发人员。首先,这个 JavaScript 性能建议与 C++ 的成员初始化规则完全无关。它也与构造函数参数的顺序无关,即从引擎性能的角度来看,以下内容完全没问题:
function Point(x, y) {
this.y = y; // Nothing wrong with this.
this.x = x;
}
"initialize members in the same order" 的意思是该类型的所有实例 应该使用相同的初始化顺序。当它们都使用相同的简单构造函数时,这会自动发生——这很好,因为这意味着在大多数情况下,您不必担心这个;你甚至不需要知道这些建议。
最常见的反例是使用对象文字创建对象,而不关心其中属性的顺序。即使 属性 名称的 set 相同,隐藏的 classes 的顺序也很重要。考虑:
let p1 = {x: 1, y: 2};
let p2 = {y: 3, x: 4};
// p1 and p2 now have different hidden classes!
function get_x(point) { return point.x; }
get_x(point1);
get_x(point2);
get_x中的point.x
加载现在需要处理两个隐藏的classes,
这比每次都看到相同的隐藏 class 稍慢。
"Always use a constructor" 是避免这种情况的一个很好的经验法则;然而,这还不够精确,如以下示例所示:
function SillyPoint(x, y) {
if (x >= y) { // or any other condition
this.x = x;
this.y = y;
} else {
this.y = y;
this.x = x;
}
}
let p1 = SillyPoint(1, 2);
let p2 = SillyPoint(2, 1);
虽然p1和p2使用了相同的构造函数,但是他们初始化了各自的
成员顺序不同,所以他们有不同的隐藏 classes。
就像上面的例子一样,这使得 属性 在需要同时处理 p1 和 p2 的函数中加载速度稍慢。
阅读此 old article 中的 V8(微)优化 并且有一句话:
Always initialize object members in the same order
问题:在下面的示例中,总是以相同的顺序初始化对象成员是什么意思?
function Point(x, y) {
this.x = x; // this.x = x line first because x is the first arg
this.y = y; // this.y = y line second because y is the second arg
}
var p1 = new Point(11, 22);
var p2 = new Point(33, 44);
// At this point, p1 and p2 have a shared hidden class
p2.z = 55;
// warning! p1 and p2 now have different hidden classes!
更长的引用:
JavaScript has limited compile-time type information: types can be changed at runtime, so it's natural to expect that it is expensive to reason about JS types at compile time. This might lead you to question how JavaScript performance could ever get anywhere close to C++. However, V8 has hidden types created internally for objects at runtime; objects with the same hidden class can then use the same optimized generated code.
Until the object instance p2 has additional member ".z" added, p1 and p2 internally have the same hidden class - so V8 can generate a single version of optimized assembly for JavaScript code that manipulates either p1 or p2. The more you can avoid causing the hidden classes to diverge, the better performance you'll obtain.
Therefore:
Initialize all object members in constructor functions (so the instances don't change type later)
Always initialize object members in the same order
注意:我在C++中发现了类似的问题,但我真的看不懂Why should I initialize member variables in the order they're declared in?
这里是 V8 开发人员。首先,这个 JavaScript 性能建议与 C++ 的成员初始化规则完全无关。它也与构造函数参数的顺序无关,即从引擎性能的角度来看,以下内容完全没问题:
function Point(x, y) {
this.y = y; // Nothing wrong with this.
this.x = x;
}
"initialize members in the same order" 的意思是该类型的所有实例 应该使用相同的初始化顺序。当它们都使用相同的简单构造函数时,这会自动发生——这很好,因为这意味着在大多数情况下,您不必担心这个;你甚至不需要知道这些建议。
最常见的反例是使用对象文字创建对象,而不关心其中属性的顺序。即使 属性 名称的 set 相同,隐藏的 classes 的顺序也很重要。考虑:
let p1 = {x: 1, y: 2};
let p2 = {y: 3, x: 4};
// p1 and p2 now have different hidden classes!
function get_x(point) { return point.x; }
get_x(point1);
get_x(point2);
get_x中的point.x
加载现在需要处理两个隐藏的classes,
这比每次都看到相同的隐藏 class 稍慢。
"Always use a constructor" 是避免这种情况的一个很好的经验法则;然而,这还不够精确,如以下示例所示:
function SillyPoint(x, y) {
if (x >= y) { // or any other condition
this.x = x;
this.y = y;
} else {
this.y = y;
this.x = x;
}
}
let p1 = SillyPoint(1, 2);
let p2 = SillyPoint(2, 1);
虽然p1和p2使用了相同的构造函数,但是他们初始化了各自的 成员顺序不同,所以他们有不同的隐藏 classes。 就像上面的例子一样,这使得 属性 在需要同时处理 p1 和 p2 的函数中加载速度稍慢。