Переполнение стека оптимизации рекурсии хвоста ES6

Прочитав описание д-ра Раушмайера рекурсивной оптимизации хвостовых вызовов в es6, С тех пор я пытался воссоздать выполнение рекурсивной факториальной функции с нулевым стеком, которую он подробно описывает.

Используя отладчик Chrome для перехода между кадрами стека, я вижу, что оптимизация хвоста не происходит, и для каждой рекурсии создается кадр стека.

Я также попытался протестировать оптимизацию, вызвав функцию без отладчика, но вместо этого передав 100000 функции факториала. Это вызывает ошибку «максимального стека», что означает, что на самом деле он не оптимизирован.

Вот мой код:

const factorial = (n, acc = 1) => n <= 1 ? acc : factorial(n - 1, n * acc)
console.log( factorial(100000) )

Результат:

Uncaught RangeError: Maximum call stack size exceeded

person Sam Hanlan    schedule 14.03.2017    source источник
comment
Дополнительно: let acc = 1, n = 1e+5; while (n > 1) acc *= n--; console.log(acc)   -  person Klaider    schedule 14.03.2017
comment
@Matheus отличное решение, красиво сделано! Я знаю, что это не рекурсивно, но это «настоящее» решение, которое действительно имеет значение, верно?   -  person Sam Hanlan    schedule 16.03.2017
comment
Спасибо, она делает то же самое, что и рекурсивная функция, да! В результате получается Infinity, но n равно 1 в конце xd   -  person Klaider    schedule 16.03.2017


Ответы (2)


V8, движок JavaScript в Chrome, некоторое время имел поддержку TCO, но на момент этого обновленного ответа (ноябрь 2017 г.) этого больше не было, и на момент написания этой статьи в V8 не ведется активной разработки по TCO и не планируется. Вы можете прочитать подробности в об ошибке отслеживания V8.

В какой-то момент поддержка TCO, похоже, достигла приличного уровня в V8, но по нескольким причинам (проблемы с отладкой, ошибки) оставалась невыполненной. Но затем произошло несколько вещей, не в последнюю очередь то, что команда V8 подняла значительные проблемы с совокупной стоимостью владения и решительно поддержал изменение спецификации под названием синтаксические хвостовые вызовы (STC), что потребовало бы, чтобы хвостовые вызовы были намеренно помечены в исходном коде (например, return continue doThat();). Однако это предложение стало неактивным в июле 2017 года. Также в июле, когда работы по TCO не проводились, команда V8 удалила код для поддержки TCO из источника TurboFan *, поскольку в противном случае он мог бы быть предметом битрота. (Например, стать проблемой обслуживания и источником ошибок.)

Таким образом, в настоящее время (ноябрь 2017 г.) неясно, будет ли «невидимая» совокупная стоимость владения когда-либо в V8, появятся ли какие-то STC или что-то еще. Страница статуса платформы Chrome для этого указывает на "смешанные" общедоступные сигналы от Mozilla (Firefox / SpiderMonkey) и Microsoft (Edge / Chakra) о поддержке совокупной стоимости владения, о том, что Safari поставляется с совокупной стоимостью владения, и что веб-разработчики "положительно" относятся к этой функции. Посмотрим, куда мы пойдем дальше. Если угодно.

* (TurboFan = текущий передовой JIT-компилятор в V8, теперь у них есть переключился с Full-Codegen [JIT] + Crankshaft [агрессивная оптимизация JIT] на Ignition [интерпретатор +] и TurboFan [агрессивная оптимизация JIT])

person T.J. Crowder    schedule 14.03.2017
comment
Я не знал об этом, гул ... так что нет особого преимущества от использования ES6 в целом только для этой оптимизации хвостовых вызовов, я думаю, что было бы лучше иметь виртуальный язык, работающий внутри (который оптимизирует хвост- вызовы во время компиляции или выполнения). - person Klaider; 14.03.2017
comment
Что ж, для оптимизации хвостовых вызовов на внутреннем языке, предположим, что это ES3, можно было бы избежать создания следующих областей видимости и т.д ... как вы думаете, это возможно? - person Klaider; 14.03.2017
comment
К сожалению, в Chrome (и, соответственно, в Интернете), вероятно, никогда не будет надлежащей реализации хвостового вызова. См. Ответ ниже. - person Freewalker; 09.11.2017
comment
@ T.J. Crowder Вы правы, возможно, он не совсем мертв - на этой встрече WebAssembly ни одна из присутствующих групп не проголосовала против дальнейшего рассмотрения (все были нейтральными или положительными). Спасибо за побуждение посмотреть еще немного! github.com/WebAssembly/meetings/blob/ мастер / 2017 / - person Freewalker; 09.11.2017
comment
@ captain-yossarian - я ничего не слышал. Насколько я могу судить, это все еще тупиковая ситуация: JavaScriptCore реализует их, а V8 и SpiderMonkey - нет. Я не думаю, что кто-то заинтересован в том, чтобы довести его до разрешения, каким-либо из способов его решения (удаление их из спецификации, V8 и SpiderMonkey, реализующие их как есть, или замена их явным синтаксисом [который Apple не понравился в 2016 году]). Это иллюстрирует мудрость текущих предложений по пути (которые предшествуют последним вызовам), когда в спецификации ничего не говорится, пока не будет 2+ реализаций. - person T.J. Crowder; 05.10.2020

Команда V8 (движок JS Chrome) пока не реализует TCO. Он взят из самых последних версий (см. Эту ветку ).

Из основных браузеров только Safari фактически реализовал эту функцию.

В Node.JS версии 8 и новее TCO недоступна.

Есть надежда на реализацию TCO: в Встреча WebAssembly 2017, Google и все другие присутствующие группы нейтрально или положительно отнеслись к дальнейшему изучению реализации TCO.

person Freewalker    schedule 09.11.2017
comment
Я обновил сообщение, чтобы упомянуть V8 вместо Chromium, спасибо. Однако я думаю, что упомянутая ветка довольно ясно показывает, что, если не считать дальнейших разработок, это мертв в воде. Я хотел бы услышать, есть ли другая информация! Лично мне определенно нужна эта функция в JS. Проверка V8 в июле с удалением всей работы TCO говорит: реализация хвостового вызова ... известно, что она не работает во многих случаях, включая проблемы с безопасностью clusterfuzz (см. Образец проблем Chromium ниже ). Этот патч удаляет эту возможность, чтобы не допустить дальнейшей обработки битов реализации по стволу. - person Freewalker; 09.11.2017
comment
@ T.J. Crowder Действительно интересно, ценю обсуждение! Мы продолжаем двигаться дальше к FP повсюду, и это был бы действительно хороший шаг. - person Freewalker; 09.11.2017
comment
comment
@ T.J. Crowder благодарит за то, что поднял эту тему. Это действительно большое разочарование! - person ; 10.11.2017
comment
Просто протестировал его в Safari (12.0.2) с факториалом (100000) и получил исключение Превышен максимальный размер стека вызовов. [ссылка] jsfiddle.net/j076L5v8/1 - person Ofir Meguri; 04.04.2019
comment
Ссылка на встречу WebAssembly мертва. :( - person jamesh625; 11.09.2020
comment
@ jamesh625 Обновлено, спасибо, похоже, он был удален из репо, но все еще присутствует в предыдущей версии репо. - person Freewalker; 11.09.2020