Некоторое время я возился с шахматным движком Javascript. Да, да, я знаю (смеется), не самая лучшая платформа для такого рода вещей. Это что-то вроде домашнего проекта, я наслаждаюсь академическими упражнениями и заинтригован проблемой приближения к скомпилированным языковым скоростям. В Javascript есть и другие причудливые проблемы, такие как отсутствие 64-битных целых чисел, которые делают его непригодным для игры в шахматы, но также парадоксально интересным.

Некоторое время назад я понял, что чрезвычайно важно быть осторожным с конструкциями, параметрами функций и т. Д. Все имеет значение в шахматном программировании, но, похоже, многое имеет значение при работе с JIT-компиляторами (V8 Turbofan) через Javascript в Chrome.

Через некоторые следы я вижу некоторые нетерпеливые DEOPT-ы, из-за которых мне сложно понять, как их избежать.

DEOPT рвется, неправильная карта

Код, на который ссылается трассировка:

if (validMoves.length) { ...do some stuff... } 

Трассировка указывает непосредственно на аргумент validMoves.length условного IF. validMoves — это только пустой массив [] или массив объектов перемещения [{Move}, {Move}, …]

Будет ли пустой массив [] запускать DEOPT?

Между прочим, у меня много ленивых и мягких DEOPT, но если я правильно понимаю, это не так важно, и это лишь часть того, как V8 оборачивает мой код, прежде чем в конечном итоге оптимизировать его; в —trace-opt функции с мягкими, ленивыми DEOPT-ами, по-видимому, в конечном итоге оптимизируются Turbofan и, возможно, не сильно влияют на производительность в долгосрочной перспективе. (В этом отношении, похоже, что рьяные функции DEOPT в конечном итоге тоже подвергаются повторной оптимизации.) Это правильная оценка?

Наконец, я иногда обнаруживал, что, разбивая функции, которые показывают DEOPT, на несколько меньших вызовов функций, я получал заметный прирост производительности. Из этого я сделал вывод, что более крупные и сложные функции испытывают трудности с оптимизацией и что, разбивая их, меньшие разделенные функции оптимизируются и, таким образом, подпитывают мои выгоды. Это звучит разумно?

отсутствие 64-битных целых

Ну, теперь есть BigInts :-) (Но в большинстве двигателей / корпусов они пока не подходят для высокопроизводительных операций.)

Будет ли пустой массив [] запускать DEOPT?

Вообще нет. Однако существуют разные внутренние представления массивов, так что они могут или не могут быть тем, что там происходит.

[ленивый, мягкий, нетерпеливый …] Это правильная оценка?

Вообще да. Обычно вам не нужно беспокоиться об удалениях, особенно для долгосрочных программ, которые на ранних этапах сталкиваются с несколькими отклонениями. Это верно для всех прилагательных, --trace-deopt сообщает --trace-deopt — это всего лишь внутренние детали. («нетерпеливый» и «ленивый» являются прямыми противоположностями друг друга и просто указывают, была ли активация функции, которая должна была быть деоптимизирована, вершиной стека или нет. «мягкий» является частной причиной для отказа, а именно отсутствие обратной связи по типу, и V8 выбрал деоптимизацию вместо генерации «оптимизированного» кода, несмотря на отсутствие обратной связи по типу, что было бы совсем не оптимизировано.)

Есть очень немного случаев, когда вы, как разработчик JavaScript, возможно, захотите позаботиться о deopts. Одним из примеров является случай, когда один и тот же случай повторяется снова и снова. Это ошибка в V8, когда это происходит; эти «петли деопта» редки, но иногда они случаются. Если вы обнаружили такой случай, пожалуйста, сообщите об ошибке с инструкциями по воспроизведению.

Другой случай, когда каждый цикл ЦП имеет значение, особенно во время запуска / в приложениях с коротким запуском, и некоторые дорогостоящие функции деоптимизируются по причине, которую можно было бы избежать. Хотя это не похоже на ваш случай.

[разбивая функции …] Это звучит разумно?

Разрушение функций может быть полезным, да; особенно если функции, с которых вы начали, были огромными. Как правило, функции всех размеров оптимизируются; очевидно, большие функции занимают больше времени для оптимизации. Это сложная область без простых ответов; если функции слишком малы, то это тоже не полезно для производительности. V8 выполнит некоторую вставку, но решения основаны на эвристике, которая, естественно, не всегда идеальна. По моему опыту, функции разделения вручную могут, в частности, окупиться за длительные циклы (где вы бы поместили цикл в свою собственную функцию).