Здравствуйте, Codealot, Вы писали:
C>Это еще что за зверь?
Кто, let o = {sum: 0}? Обычный объект со свойством sum, только объявленный в литеральной форме (JSON же знаешь как выглядит?). Создается сразу при объявлении.
console.log("Пустой при объявлении объект");
var obj1 = {};
console.log(obj1);
// Добавить свойства и методы
obj1.prop1 = "значение Prop1";
obj1.getProp1 = function() {
return this.prop1;
}
console.log(obj1.getProp1());
console.log(obj1);
console.log("Тоже, но сразу со свойствами и методами");
var obj2 = {
prop1: "значение Prop1",
getProp1: function() {
return this.prop1;
}
}
console.log(obj2.getProp1());
console.log(obj2);
Здравствуйте, rFLY, Вы писали:
FLY>А если локальную переменную заменить на локальный объект (let sum = 0 на let o = {sum: 0})
FLY>то отработает еще на 120 быстрее (интел, эдж последней версии)
У меня ноутбучный проц i7 22го года, отрабатывает за 900 и 600, т.е. в полтора раза стало быстрее.
А есть какое внятное объяснение этому? inline cache включается или что?
Здравствуйте, Pauel, Вы писали:
P>А есть какое внятное объяснение этому? inline cache включается или что?
Вроде в ноде можно дизассемблировать при помощи следующего флага и посмотреть что там творится.
node --print_opt_code test.js
У меня тут ноды нет, так что посмотреть не могу. Могу лишь предположить, что значение свойства sum записывается в регистр, а после цикла обратно, в то время как при работе с переменной при каждой итерации используется ее адрес. А может и еще что делается.
UPD: Стало интересно и решил поэксперементировать
class BaseClass
{
constructor()
{
this.Value = BaseClass.Counter++;
}
TestMethod()
{
return this.Value;
}
static Counter = 0;
}
console.log("без создания объекта");
{
let watch = performance.now();
let sum = 0;
for (let i = 0; i < 100_000_000; i++)
sum += i;
console.log(`var_: ${performance.now() - watch}`);
}
{
let watch = performance.now();
let o = {sum: 0};
for (let i = 0; i < 100_000_000; i++)
o.sum += i;
console.log(`prop: ${performance.now() - watch}`);
}
console.log("с созданием объекта");
{
let watch = performance.now();
let sum = 0;
for (let i = 0; i < 100_000_000; i++) {
new BaseClass();
sum += i;
}
console.log(`var_: ${performance.now() - watch}`);
}
{
let watch = performance.now();
let o = {sum: 0};
for (let i = 0; i < 100_000_000; i++) {
new BaseClass();
o.sum += i;
}
console.log(`prop: ${performance.now() - watch}`);
}
без создания объекта
var_: 58.90000009536743
prop: 200.90000009536743
с созданием объекта
var_: 326.1000003814697
prop: 223.5
То есть на итог влияет new BaseClass(). С ним, даже если объект не используется, суммирование через переменную существенно просаживается.
Здравствуйте, rFLY, Вы писали:
FLY>То есть на итог влияет new BaseClass(). С ним, даже если объект не используется, суммирование через переменную существенно просаживается.
сумма — короткая операция. Вы, условно, умножаете издержки просто дёргая new BaseClass();
new BaseClass — выделение памяти, инициализация
BaseClass.Counter — лукап
++ — сравнимо с += в основном цикле
this.Value — еще один лукап
Здравствуйте, Pauel, Вы писали:
P>new BaseClass — выделение памяти, инициализация P>BaseClass.Counter — лукап P>++ — сравнимо с += в основном цикле P>this.Value — еще один лукап
Ты не понял или не вникал в написанное. Создание объекта не просто приводит к замедлению, а меняет время выполнения (и только при использовании переменной для суммы) на противоположный. При вычислении суммы через свойство объекта о.sum различия во времени минимальны. Повторю укороченно, если код проигнорировал:
// без new BaseClass()let sum = 0;
for (let i = 0; i < 100_000_000; i++)
sum += i;
// и
let o = {sum: 0};
for (let i = 0; i < 100_000_000; i++)
o.sum += i;
// против следующего, но с добавлением new BaseClass()let sum = 0;
for (let i = 0; i < 100_000_000; i++) {
new BaseClass();
sum += i;
}
// и
let o = {sum: 0};
for (let i = 0; i < 100_000_000; i++) {
new BaseClass();
o.sum += i;
}
}
вычисление переменной let sum c new BaseClass становится дороже чем аналогичное, но через o.sum, а без new BaseClass() наоборот.
Здравствуйте, rFLY, Вы писали:
FLY>вычисление переменной let sum c new BaseClass становится дороже чем аналогичное, но через o.sum, а без new BaseClass() наоборот.
Что тут странного? Джит крайне ограничен, во всех платформах. чуть не так — отваливается та или иная оптимизация.
Здравствуйте, Codealot, Вы писали:
C>Мда, ну и муть.
Почему же, просто какой-то косяк в движке. Причем связанный с обращением к статическому свойству в конструкторе через имя класса:
class BaseClass
{
constructor()
{
this.Value = this.constructor.Counter++;
}
TestMethod()
{
return this.Value;
}
static Counter = 0;
}
var func = function()
{
var sum = 0;
for (var i = 0; i < 100_000_000; i++)
{
var obj = new BaseClass();
var val = obj.TestMethod();
sum += val;
}
console.log(sum);
};
{
var watch = performance.now();
func();
var time = performance.now() - watch;
console.log(`1: ${time}`);
}
BaseClass.Counter = 0;
{
var watch = performance.now();
let sum = 0;
for (let i = 0; i < 100_000_000; i++)
{
let obj = new BaseClass();
let val = obj.TestMethod();
sum += val;
}
console.log(sum);
var time = performance.now() - watch;
console.log(`2: ${time}`);
}
Здравствуйте, Pauel, Вы писали:
P>Что тут странного? Джит крайне ограничен, во всех платформах. чуть не так — отваливается та или иная оптимизация.
А что в sum += i оптимизировать? Впрочем причина нашлась, если интересно см. ответ выше.
Здравствуйте, rFLY, Вы писали:
P>>Что тут странного? Джит крайне ограничен, во всех платформах. чуть не так — отваливается та или иная оптимизация. FLY>А что в sum += i оптимизировать? Впрочем причина нашлась, если интересно см. ответ выше.
Я бы предположил, что джит делает не любые возможные оптимизации, а только те, на которые у него капасити хватает.
Здравствуйте, rFLY, Вы писали:
FLY>Здравствуйте, Codealot, Вы писали:
C>>Мда, ну и муть. FLY>Почему же, просто какой-то косяк в движке. Причем связанный с обращением к статическому свойству в конструкторе через имя класса:
Это не косяк, это тот же lookup, что и с поиском в глобальном скопе
Вот смотрите — если вместо class BaseClass написать const BaseClass = class { ... то получится ровно то же, что и у вас
Смотрите сами
без создания объекта
var_: 67.20000000018626
prop: 208.29999999981374
с созданием объекта
var_: 89.90000000037253
prop: 203.90000000037253
Собственно, если просто заверуть всё в функцию, это ничем не помогает. Все дело именно в том, куда запихивается class BaseClass. Похоже, это какой то конский объект.
А вот объявление класса через const меняет дело
const BaseClass = class
{
constructor()
{
this.Value = BaseClass.Counter++;
}
TestMethod()
{
return this.Value;
}
static Counter = 0;
}
console.log("без создания объекта");
{
let watch = performance.now();
let sum = 0;
for (let i = 0; i < 100_000_000; i++)
sum += i;
console.log(`var_: ${performance.now() - watch}`);
}
{
let watch = performance.now();
let o = {sum: 0};
for (let i = 0; i < 100_000_000; i++)
o.sum += i;
console.log(`prop: ${performance.now() - watch}`);
}
console.log("с созданием объекта");
{
let watch = performance.now();
let sum = 0;
for (let i = 0; i < 100_000_000; i++) {
new BaseClass();
sum += i;
}
console.log(`var_: ${performance.now() - watch}`);
}
{
let watch = performance.now();
let o = {sum: 0};
for (let i = 0; i < 100_000_000; i++) {
new BaseClass();
o.sum += i;
}
console.log(`prop: ${performance.now() - watch}`);
}
Здравствуйте, Pauel, Вы писали:
P>Это не косяк, это тот же lookup, что и с поиском в глобальном скопе
Да, но в таком случае обращение к BaseClass.Counter не должно делать sum += value медленнее чем obj.sum += value. Почему высчитывание суммы через свойство объекта вдруг стало производительнее чем через обычную переменную? И там и там лукап в конструкторе класса и время просчета суммы для обоих способов должно было измениться пропорционально. Вот в этом я вижу косяк.
Здравствуйте, rFLY, Вы писали:
FLY>Да, но в таком случае обращение к BaseClass.Counter не должно делать sum += value медленнее чем obj.sum += value. Почему высчитывание суммы через свойство объекта вдруг стало производительнее чем через обычную переменную? И там и там лукап в конструкторе класса и время просчета суммы для обоих способов должно было измениться пропорционально. Вот в этом я вижу косяк.
Это поведение очень похоже на работу inline cache
У него капасити крохотный, +1 лишний лукап и уже отставание в разы
Здравствуйте, Pauel, Вы писали:
P>Покажите код, у меня чтото не получается воспроизвести
282.75
1442.3100000023842
Только в хроме, в фаерфоксе такого эффекта нет.
'use strict';
const TestsNumber = 10;
const ItemsNumber = 100_000_000;
function Test(func)
{
let totalTime = 0;
for (let i = 0; i < TestsNumber; i++)
{
let watch = performance.now();
func();
let curTime = performance.now() - watch;
totalTime += curTime;
}
console.log(totalTime / TestsNumber);
}
class BaseClass
{
TestMethod()
{
this.Value = BaseClass.Counter++;
return this.Value;
}
static Counter = 0;
}
Test(function()
{
let obj = { sum: 0 };
for (let i = 0; i < ItemsNumber; i++)
{
let obj = new BaseClass();
let val = obj.TestMethod();
obj.sum += val;
}
});
Test(function()
{
let sum = 0;
for (let i = 0; i < ItemsNumber; i++)
{
let obj = new BaseClass();
let val = obj.TestMethod();
sum += val;
}
});
Здравствуйте, Codealot, Вы писали:
C>Похоже, не только в конструкторе. И еше если использовать C>
C>let obj = { sum: 0 };
C>
C>а потом C>
C>let sum = 0;
C>
C>(или наоборот), то производительность второго варианта просаживается раз в 5.
Это следствие, а не причина. Если изменить конструктор (то, о чем писал я) или использовать модификатор const в объявлении класса (как предложил Pauel), то все начинает работать ожидаемо.