Хороший вводный текст о реализации GHC?

При программировании в Haskell (и особенно при решении проблем Project Euler, где субоптимальные решения имеют тенденцию подчеркивать потребности в ЦП или памяти), я часто озадачиваюсь, почему программа ведет себя так, как она есть. Я просматриваю профили, стараюсь ввести некоторую строгость, выбираю другую структуру данных... но в основном это нащупывает в темноте, потому что мне не хватает хорошей интуиции.

Кроме того, хотя я знаю, как Lisp, Prolog и императивные языки, как правило, реализуются, я понятия не имею о реализации ленивого языка. Мне тоже немного любопытно.

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

Что мне интересно:

  • какие типичные оптимизации применяются?

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

  • как изображены thunks?

  • Как используются стек и куча?

  • что такое CAF? (профилирование иногда указывает на то, что точка доступа есть, но я не знаю)

Ответ 1

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

Какие типичные оптимизации применяются?

Ключевой документ по этому вопросу: Оптимизатор, основанный на преобразовании для Haskell, SL Peyton Jones and A Santos, 1998, в котором описываются модели использования GHC применения преобразований сохранения типа (рефакторинга) основного языка, подобного Haskell, для улучшения использования времени и памяти. Этот процесс называется "упрощением".

Типичные вещи, которые выполняются в компиляторе Haskell, включают:

  • Встраивание;
  • Бета-сокращение;
  • Исключение мертвого кода;
  • Трансформация условий: случайный случай, исключение случая.
  • распаковка;
  • Возврат построенного продукта;
  • Полная трансформация лени;
  • Специализация;
  • Расширение Eta;
  • Лямбда-подъем;
  • Анализ стесненности.

И иногда:

  • Преобразование статического аргумента;
  • Сборка/слияние или слияние потоков;
  • Исключение общей суб-экспрессии;
  • Специализация конструктора.

Вышеупомянутая статья - ключевое место, чтобы начать понимать большинство этих оптимизаций. Некоторые из более простых из них приведены в предыдущей книге, Реализация функциональных языков, Саймон Пейтон Джонс и Дэвид Лестер.

Что такое порядок выполнения при наличии нескольких кандидатов для оценки

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

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

Как представлены трюки?

Thunks представлены как данные, выделенные кучей, с помощью указателя кода.

Heap object

См. расположение объектов кучи. В частности, см. как представлены thunks.

Как используется стек и куча?

Как определено дизайном Spineless Tagless G-machine, в частности, со многими модификациями с момента выхода этой статьи. В целом, модель исполнения:

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

Чтобы глубоко понять модель использования стека, см. "Push/Enter versus Eval/Apply" .

Что такое CAF?

A "Постоянная аппликативная форма". Например. константу верхнего уровня в вашей программе, выделенную на время выполнения вашей программы. Поскольку они распределены статически, они должны быть обработаны специально сборщиком мусора.


Ссылки и дополнительное чтение:

Ответ 2

Это, вероятно, не то, что вы имели в виду с точки зрения вводного текста, но у Эдварда Янга есть постоянная серия сообщений в блогах, обсуждающих кучу Haskell, как реализуются thunks и т.д.

Это развлекательно, как с иллюстрациями, так и в силу объяснения вещей, не вникая в слишком подробные сведения для кого-то нового для Haskell. Серия охватывает многие из ваших вопросов:

На более техническом уровне есть ряд документов, которые охватывают (в согласии с другими) части того, что вы хотите знать.

  • Статья SPJ, Simon Marlow и др. о GC в Haskell - я ее не читал, но поскольку GC часто представляет собой хороший портфон работы Haskell, он должен дать представление.
  • Отчет Haskell 2010 - Я уверен, что вы узнаете об этом, но это слишком хорошо, чтобы не ссылаться. Может делать для сухого чтения в местах, но один из лучших способов понять, что делает Haskell таким, каким он есть, по крайней мере, части, которые я читал.
  • История Haskell - более технична, чем предполагалось в названии, и предлагает некоторые очень интересные взгляды на дизайн Haskell, а также решения, стоящие за дизайн. Вы не можете помочь, но лучше понять реализацию Haskell после прочтения.