fullstack-development / haskell-starter-kit

Starter Kit for web-backend apps written with Haskell, Servant, Docker and PostgreSQL
BSD 3-Clause "New" or "Revised" License
1 stars 1 forks source link

Медленная сборка #18

Closed antonkalinin-ml closed 3 years ago

antonkalinin-ml commented 3 years ago

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

ghc-options:
  "$everything": -j

Тогда сборка ускорится и в докере, и на ручном вызове stack build. К сожалению, это перебьет ghc-options, установленные глобально в ~/.stack/config.yaml, если они там есть (у меня есть), и все зависимости числом 170 штук перебилдятся. Но если этим пользуюсь только я, то нестрашно, сделаю как у всех.

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

Что думаете, какой вариант лучше?

Еще вопрос на подумать: stack по умолчанию билдит все с опцией оптимизации -O. Слишком долго для отладочных билдов - опция -O0 билдит в 2-3 раза быстрее, недостаточно оптимально для продакшена - есть еще -O2 (хотя пишут, что -O2 от -O мало отличается). Я себе поставил в глобальном конфиге -O2 для $everything и -O0 для $locals. Так зависимости, которые билдятся и меняются редко, оптимизируются по максимуму, а проект, который меняется часто, билдится максимально быстро. stack build --fast также активирует -O0, но сколько людей знают эту опцию и не поленятся ее вводить? Мне видится, что дефолтный режим должен быть без оптимизации как самый простой, а релизные сборки можно сделать и с дополнительным ключом, все равно это делает пайплайн и разная автоматика. Как вы обычно делаете в других проектах?

Znack commented 3 years ago

О, вот это хороший поинт. На самом деле мне пока вариант с настройкой чисто для докера нравится больше, а для глобальных конфигов мы можем в README добавить пару строчек — новички оттуда могут узнать и каждый для себя по желанию сам настроит :)

antonkalinin-ml commented 3 years ago

Протестировал с разными опциями. Похоже, ничего делать не надо, настройки по умолчанию оптимальны (если не считать stack build --fast), а я себе сделал только хуже :).

Опция ghc -j для сборки зависимостей это какая-то катастрофа (не путать со stack build -j). С ней процессор отлично загружен на 100% на всех ядрах, зато зависимости билдятся в 4 раза дольше.

В докере этап stack build --only-dependencies раньше занимал 8 минут: real 8m13.865s user 25m33.870s sys 2m0.827s

После добавления строчки RUN /bin/echo -e 'ghc-options:\n "$everything": -j' >> "$(stack path --stack-root)"/config.yaml стало 30 минут:

real 29m51.011s user 348m2.216s sys 91m54.456s

Железо: AMD 4750U, 8 ядер, 16 потоков, 32 Гб RAM. Без докера тоже воспроизводится. SMT отключал, скорость немного подросла - чем меньше потоков, тем быстрее. Абсурд какой-то.

На сборке самого проекта польза от опции есть, но очень незначительная, в районе 10-20%. Больше толку от stack build --fast.

antonkalinin-ml commented 3 years ago

Сохраню ссылку для истории, как немного ускорить многопоточную сборку: https://rybczak.net/2016/03/26/how-to-reduce-compilation-times-of-haskell-projects/ . Действительно помогает, но при сборке большого количества мелких пакетов (160 зависимостей) однопоточный режим все равно быстрее. Эта ссылка пригодится для подкручивания опций сборки одного большого проекта.

Znack commented 3 years ago

Как минимум можно в Makefile добавить шорткат для stack build --test --file-watch --fast --pedantic, чтобы те кто не знали, сразу его увидели в файле и заюзали :)