Как реализовать продолжения?

Я работаю над интерпретатором Scheme, написанным на C. В настоящее время он использует стек среды выполнения C в качестве собственного стека, что создает небольшую проблему с реализацией продолжений. Моим текущим решением является ручное копирование стека C в кучу, а затем копирование его обратно при необходимости. Помимо того, что это решение не является стандартным C, это решение едва ли можно назвать идеальным.

Каков самый простой способ реализовать продолжения для схемы в C?


person Kyle Cronin    schedule 09.08.2008    source источник


Ответы (12)


Я помню, как читал статью, которая может быть вам полезна: Чейни о MTA :-)

Некоторые известные мне реализации Scheme, такие как SISC, размещают свои кадры вызовов в куче.

@ollie: вам не нужно поднимать, если все ваши кадры вызовов находятся в куче. Конечно, есть компромисс в производительности: время на подъем по сравнению с накладными расходами, необходимыми для выделения всех кадров в куче. Возможно, это должен быть настраиваемый параметр времени выполнения в интерпретаторе. :-П

person Chris Jester-Young    schedule 09.08.2008

Хорошее резюме доступно в статье Implementation Strategies for First Class Continuations Клингер, Хартхаймер и Ост. Я рекомендую обратить внимание на реализацию Chez Scheme, в частности.

Копирование стека не так уж сложно, и существует ряд хорошо изученных методов повышения производительности. Использование кадров, выделенных в куче, также довольно просто, но вы идете на компромисс, создавая накладные расходы для «нормальной» ситуации, когда вы не используете явные продолжения.

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

person Josh Segall    schedule 31.08.2008

Если вы начинаете с нуля, вам действительно следует обратить внимание на преобразование стиля передачи продолжения (CPS).

Хорошие источники включают «LISP в небольших частях» и minute-en.html" rel="noreferrer">Схема Марка Фили в 90-минутной презентации.

person Joel Borggrén-Franck    schedule 05.12.2008
comment
Книга Queinnec Lisp In Small Pieces доступна (по крайней мере, во французском издании Paracampus) - person Basile Starynkevitch; 30.01.2016

Похоже, что тезис Дибвига пока не упоминается. Это удовольствие читать. Модель на основе кучи проще всего реализовать, но стековая модель более эффективна. Игнорируйте модель на основе строк.

Р. Кент Дибвиг. «Три модели реализации схемы». http://www.cs.indiana.edu/~dyb/papers/3imp.pdf

Также ознакомьтесь с документами по реализации на ReadScheme.org. https://web.archive.org/http://library.readscheme.org/page8.html

Аннотация выглядит следующим образом:

В данной диссертации представлены три модели реализации языка программирования Scheme. Первая — это модель на основе кучи, используемая в той или иной форме в большинстве реализаций Scheme на сегодняшний день; вторая — новая модель на основе стека, которая значительно более эффективна, чем модель на основе кучи, при выполнении большинства программ; и третья — новая модель на основе строк, предназначенная для использования в многопроцессорной реализации Scheme.

Модель на основе кучи размещает в куче несколько важных структур данных, включая фактические списки параметров, среды привязки и кадры вызовов.

Модель на основе стека по возможности размещает те же самые структуры в стеке. Это приводит к меньшему выделению кучи, меньшему количеству ссылок на память, более коротким последовательностям инструкций, меньшей сборке мусора и более эффективному использованию памяти.

Строковая модель размещает версии этих структур прямо в тексте программы, который представлен в виде строки символов. В модели на основе строк программы Scheme переводятся на язык FFP, специально разработанный для поддержки Scheme. Программы на этом языке непосредственно выполняются машиной FFP, многопроцессорным компьютером для сокращения строк.

Модель на основе стека имеет немедленную практическую пользу; это модель, используемая авторской системой Chez Scheme, высокопроизводительной реализацией Scheme. Модель на основе строк будет полезна для предоставления Scheme в качестве высокоуровневой альтернативы FFP на машине FFP после того, как машина будет реализована.

person soegaard    schedule 30.10.2011

Помимо хороших ответов, которые вы получили до сих пор, я рекомендую Эндрю Аппеля Компиляция с продолжениями. Он очень хорошо написан и, хотя не имеет прямого отношения к C, является источником действительно хороших идей для авторов компиляторов.

В Куриной Вики также есть страницы, которые могут показаться вам очень интересными, например, внутренняя структура и процесс компиляции (где CPS объясняется на реальном примере компиляции ).

person Jay    schedule 08.06.2010
comment
Мне очень нравится книга Аппеля. Одним из бонусов является то, что вы можете обратиться к исходному коду компилятора SML/NJ, который является довольно хорошим живым примером процесса, описанного Аппелем в книге. - person Nate C-K; 19.03.2015

Примеры, на которые вы можете обратить внимание: Chicken (реализация Scheme, написанная на C, которая поддержка продолжений); On Lisp Пола Грэма, где он создает преобразователь CPS для реализации подмножества продолжений в Common Lisp; и Weblocks — веб-фреймворк, основанный на продолжении, который также реализует ограниченную форму продолжений в Общий Лисп.

person Kyle Burton    schedule 25.09.2008
comment
какую главу On Lisp вы имеете в виду? - person Will Ness; 22.08.2018
comment
Глава 20 посвящена продолжениям, в частности 20.3. - person Kyle Burton; 25.08.2018

Продолжения не проблема: вы можете реализовать их с обычными функциями более высокого порядка, используя CPS. Проблема с наивным распределением стека заключается в том, что хвостовые вызовы никогда не оптимизируются, что означает, что вы не можете быть схемой.

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

Существует некоторый код, иллюстрирующий обе эти идеи.

person Charles Stewart    schedule 03.12.2009

Традиционный способ — использовать setjmp и longjmp, хотя и с некоторыми оговорками.

Вот достаточно хорошее объяснение< /а>

person Thomas Vander Stichele    schedule 28.08.2008

Продолжения в основном состоят из сохраненного состояния стека и регистров ЦП в момент переключения контекста. По крайней мере, вам не нужно копировать весь стек в кучу при переключении, вы можете только перенаправить указатель стека.

Продолжения тривиально реализуются с помощью волокон. http://en.wikipedia.org/wiki/Fiber_%28computer_science%29 . Единственное, что требует тщательной инкапсуляции, — это передача параметров и возвращаемые значения.

В Windows волокна создаются с использованием семейства вызовов CreateFiber/SwitchToFiber. в Posix-совместимых системах это можно сделать с помощью makecontext/swapcontext.

В boost::coroutine есть работающая реализация сопрограмм для C++, которая может служить ориентиром для реализации.

person Stefan Dragnev    schedule 04.01.2010
comment
тривиально реализовано... требует тщательной инкапсуляции - в этом абзаце присутствует определенная напряженность. Этот ответ будет улучшен с помощью ссылки на какой-либо код. - person Charles Stewart; 12.04.2011

Как указал soegaard, основной ссылкой остается R. Kent Dybvig. "Three Implementation Models for Scheme".

Идея в том, что продолжение — это замыкание, которое сохраняет свой стек управления оценкой. Стек управления необходим для того, чтобы продолжить оценку с момента создания продолжения с помощью call/cc.

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

Код суммирует первые 1000 чисел 1+2+3+...+1000.

(call-with-current-continuation 
 (lambda (break)
   ((lambda (s) (s s 1000 break))
    (lambda (s n cc)
      (if (= 0 n)
          (cc 0)
          (+ n
             ;; non-tail-recursive,
             ;; the stack grows at each recursive call
             (call-with-current-continuation
              (lambda (__)
                (s s (- n 1) __)))))))))

Если вы переключитесь с 1000 на 100 000, код потратит 2 секунды, а если вы увеличите введенное число, он вылетит.

person alinsoar    schedule 25.10.2017

Вместо этого используйте явный стек.

person Patrick    schedule 09.08.2008
comment
-1: Явный стек - это что? Структура данных с выделенной кучей, моделирующая стек? Распределенная в куче структура данных, моделирующая историю использования стека? Актуальность вопроса? - person Charles Stewart; 26.12.2009

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

Это в основном то же самое, что необходимо для поддержки замыканий в языках, которые их поддерживают (замыкания и продолжения несколько связаны).

person olliej    schedule 09.08.2008
comment
Но, чтобы поддержать замыкания, не могли бы вы просто сделать лямбда-лифтинг? - person apg; 01.10.2008