Будет ли в конечном итоге среда JavaScript восстановлена ​​после изменения [[Prototype]] объекта?

Итак, я прочитал отказ от ответственности и предупреждения MDN. я прочитал отличный ответ по теме, но я все еще хочу кое-что узнать. Этот вопрос на самом деле возник из ответа, который я дал на другой вопрос, здесь.

Допустим, я решил сделать черное дело. То, о чем я буду жалеть всю оставшуюся жизнь. Что-то, что навсегда запятнает меня стыдом и опозорит мою фамилию. Целенаправленное, преднамеренное окончание...

Ладно, хватит об этом. Во всяком случае, вот:

let proto = Object.getPrototypeOf(Function.prototype);

Object.setPrototypeOf(Function.prototype, {
  iBetterHaveAGoodReasonForDoingThis : "Bacon!"
});

//just to prove it actually worked
let f = (function(){});
console.log(f.iBetterHaveAGoodReasonForDoingThis);

// Quick, hide the evidence!!
Object.setPrototypeOf(Function.prototype, proto);

По сути, то, что я там сделал, это изменил прототип Function.prototype, объекта, который влияет практически на каждый фрагмент кода JavaScript, который вы можете написать. Потом поменял обратно.

Я хотел проиллюстрировать большое изменение в цепочке прототипов, которое повлияет на большую часть кода и приведет к тому, что многие оптимизации пойдут насмарку. Я не ожидаю, что изменение его обратно что-то исправит (во всяком случае, я ожидаю, что это ухудшит производительность). Я хотел бы знать, будет это или нет, но если это произойдет, это не было моим намерением.

Я просто хочу знать, начнет ли после такого изменения среда JavaScript восстанавливаться и снова начинать оптимизацию? Или он просто сдастся навсегда и будет запускать все в деоптимизированном режиме? Существуют ли оптимизации, которые никогда не будут достигнуты из-за этого? Могу ли я верить, что в конце концов, после периода восстановления, он вернется в свое обычное состояние?

Для контекста я говорю о таких движках, как самая последняя версия V8, а не о примитивном дерьме, используемом такими вещами, как Internet Explorer. Я понимаю, что ответ может быть разным в разных системах, но я надеюсь, что между ними есть что-то общее.


person GregRos    schedule 14.02.2018    source источник
comment
На самом деле, это один из вопросов, которые я давно думаю... я думаю, что команде V8 определенно нужно больше делиться своими усилиями по оптимизации, это что-то вроде черного ящика (удивительного быстрого черного ящика)...   -  person Jonas Wilms    schedule 14.02.2018
comment
Эй, бекон это всегда хороший повод!   -  person Bergi    schedule 14.02.2018
comment
Я надеюсь на экспертный ответ от jmrk, но я почти уверен, что оптимизатор никогда не сдается и в конечном итоге все выздоровеет.   -  person Bergi    schedule 14.02.2018


Ответы (1)


Разработчик V8 здесь. Этот вопрос не имеет простого ответа.

Большинство оптимизаций "вернутся" (разумеется, за счет дополнительного процессорного времени). Например, оптимизированный код, который пришлось выбросить, в конечном итоге будет перекомпилирован.

Некоторые оптимизации останутся отключенными навсегда. Например, V8 пропускает определенные проверки, когда (и пока) он знает, что цепочки прототипов не были изменены. Если он увидит, что приложение модифицирует цепочки прототипов, то с этого момента он не будет рисковать.

Чтобы еще больше усложнить ситуацию, детали могут и будут меняться со временем. (Именно поэтому нет особого смысла перечислять здесь более конкретные обстоятельства, извините.)

Фон:

В JavaScript есть много мест, где код может выполнять определенные действия, которые должен проверять движок JavaScript, но большая часть кода этого не делает. (Возьмем, к примеру, наследование отсутствующих элементов из прототипа массива: ['a', ,'c'][1] почти всегда возвращает undefined, кроме, если кто-то сделал Array.prototype[1] = 'b' или Object.prototype[1] = 'b'.) Таким образом, при создании оптимизированного кода для функции движок должен решить, между двумя вариантами:

(A) Всегда проверяйте рассматриваемую вещь (в примере: пройдите цепочку прототипов массива и проверьте каждый прототип, чтобы увидеть, есть ли в нем элемент по этому индексу). Допустим, выполнение этого кода займет 2 единицы времени.

(B) Оптимистично предположить, что прототипы массива не имеют элементов, и пропустить проверку (в примере: даже не смотреть на прототипы, просто вернуть undefined). Допустим, это сокращает время выполнения до 1 единицы времени (в два раза быстрее, ура!). Однако, чтобы быть правильным, движок теперь должен внимательно следить за цепочками прототипов всех массивов, и если где-либо обнаружатся какие-либо элементы, весь код, основанный на этом предположении, должен быть найден и выброшен за счет 1000 единицы времени.

Учитывая этот компромисс, имеет смысл, что машина сначала следует быстрой, но рискованной стратегии (B), но когда она терпит неудачу хотя бы один раз, она переключается на более безопасную стратегию (A), чтобы избежать риска снова заплатить штраф в размере 1000 единиц времени.

Вы можете спорить, является ли «хотя бы один раз» лучшим порогом, или сайт должен получить 2, 3 или даже больше бесплатных проходов, прежде чем отказаться от (B), но это не меняет фундаментального компромисса.

person jmrk    schedule 14.02.2018
comment
Отличный ответ! Спасибо. Есть ли какой-нибудь письменный материал, в котором перечислены те оптимизации, которые вы не упомянули? - person GregRos; 15.02.2018
comment
Я знаю только исходный код; и, как я уже сказал, он продолжает меняться по мере того, как над ним работает команда. - person jmrk; 15.02.2018
comment
Я предполагаю, что индексы массива - это особый случай, но разве каждый доступ к оптимизированному (именованному) свойству не выполняет проверку скрытого класса? (Я предполагаю, что эти проверки должны быть признаны недействительными, когда объект-прототип скрытого класса мутирует) - person Bergi; 15.02.2018
comment
Также меня интересует конкретно Object.setPrototypeOf. Какие предположения он на самом деле нарушает, что приводит к тому, что код выбрасывается? Конечно, когда я вызываю его для объекта-прототипа, это считается мутацией, но когда я вызываю его для нового экземпляра, разве он не должен просто изменить скрытый класс этого конкретного объекта? - person Bergi; 15.02.2018
comment
@Bergi Я думаю, что изменение прототипа вновь созданного объекта не повредит, поскольку он не использовался ни в одном коде, поэтому для него не был оптимизирован код. Я предполагаю, что в какой-то момент объект помечен как оптимизированный, и когда это так, то изменение прототипа приведет к неприятным последствиям. - person GregRos; 15.02.2018
comment
Да, при всех обращениях к свойствам выполняются проверки скрытых классов, а поскольку при изменении прототипов меняются скрытые классы, эти проверки впоследствии завершатся ошибкой. Существуют дополнительные механизмы, и кто-нибудь когда-либо модифицировал бит цепочки прототипов Array, о котором я упоминал в своем ответе, является одним из них. -- Да, изменение прототипа недавно созданного объекта оказывает меньшее влияние, чем изменение позже. Оптимизируются не объекты, а код; оптимизированный код может иметь встроенные предположения/зависимости о форме различных объектов. - person jmrk; 15.02.2018