При программировании в 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 после прочтения.