Spec-Zone.ru › Haskell 9

5.3. Оптимизация (улучшение кода)

Параметры -O* задают удобные «пакеты» флагов оптимизации; описанные далее параметры -f* задают отдельные оптимизации, которые можно включать и отключать; параметры -m* задают зависящие от конкретной машины оптимизации, которые можно включать и отключать.

Большинство этих параметров являются логическими и имеют варианты для включения и отключения (начинающиеся с префикса no-). Например, если -fspecialise включает специализацию, то -fno-specialise отключает её. Если в командной строке указано несколько флагов для одного параметра, они обрабатываются слева направо. Например, -fno-specialise -fspecialise включит специализацию.

Важно отметить, что флаги -O* примерно эквивалентны комбинациям флагов -f*. Поэтому действие флагов -O* и -f* зависит от порядка, в котором они указаны в командной строке.

Рассмотрим, например, -fno-specialise -O1. Несмотря на то, что в командной строке указан -fno-specialise, специализация всё равно будет включена. Это происходит потому, что -O1 подразумевает -fspecialise, переопределяя предыдущий флаг. В отличие от него, -O1 -fno-specialise выполнит компиляцию без специализации, как и следовало ожидать.

5.3.1. -O*: удобные «пакеты» флагов оптимизации.

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

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

Параметр типа ``-O*`` не указан: это означает: «Пожалуйста, компилируйте быстро; качество скомпилированного кода меня не слишком беспокоит». Например: ghc -c Foo.hs

-O0

Означает «отключить все оптимизации», возвращаясь к тем же настройкам, что и при отсутствии параметров -O. Указание -O0 может быть полезно, например, если make уже добавил -O в командную строку.

-O
-O1

Означает: «Генерировать качественный код, не тратя на это слишком много времени». Например: ghc -c -O Main.lhs

-O2

Означает: «Применять все безопасные оптимизации, даже если это значительно увеличит время компиляции».

Избегаемые «опасные» оптимизации — это те, которые при неудачном стечении обстоятельств могут ухудшить время выполнения или потребление памяти. Обычно их включают или отключают по отдельности.

-O⟨n⟩

Любой параметр -On, где n > 2, эквивалентен -O2.

В повседневной работе мы не используем флаг -O*. Мы используем -O, чтобы получить достойную скорость; например, когда хотим что-то измерить. Когда мы хотим выжать максимум, то обычно используем -O2 (и делаем побольше перерывов на кофе).

Самый простой способ узнать, что -O (и другие подобные параметры) «на самом деле означают», — запустить компилятор с параметром -v, а затем отойти в сторону и изумиться.

5.3.2. -f*: платформонезависимые флаги

Эти флаги включают и отключают отдельные оптимизации. Флаги, помеченные как включённые по умолчанию, по умолчанию активны на всех уровнях оптимизации, поэтому обычно нет необходимости задавать их явно. Флаг -fwombat можно отключить, указав -fno-wombat.

-fcore-constant-folding
По умолчанию:

выкл., но включается флагом -O.

Включает свёртку констант на уровне Core, то есть распространение значений, которые можно вычислить во время компиляции.

-fnum-constant-folding
По умолчанию:

выкл., но включается флагом -O.

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

Примеры:

(10 + x) + 10 ===> 20 + x

5 + x + (y + (z + (t + 5))) ===> 10 + (x + (y + (z + t)))

(5 + x) * 6 ===> 30 + 6*x

5 + x + (x + (x + (x + 5))) ===> 10 + (4 * x)

(5 + 4*x) - (3*x + 2) ===> 3 + x

-fcase-merge
По умолчанию:

выкл., но включается флагом -O.

Объединяет непосредственно вложенные выражения case, проверяющие одну и ту же переменную. Например,

case x of
   Red -> e1
   _   -> case x of
            Blue -> e2
            Green -> e3

преобразуется в

case x of
   Red -> e1
   Blue -> e2
   Green -> e2
-fcase-folding
По умолчанию:

выкл., но включается флагом -O.

Разрешает свёртку констант в выражениях case, проверяющих некоторые примитивные операции. Например,

case x `minusWord#` 10## of
   10## -> e1
   20## -> e2
   v    -> e3

преобразуется в

case x of
   20## -> e1
   30## -> e2
   _    -> let v = x `minusWord#` 10## in e3
-fcall-arity
По умолчанию:

выкл., но включается флагом -O.

Включает анализ арности вызовов.

-fexitification
По умолчанию:

выкл., но включается флагом -O.

Включает вынос путей выхода из рекурсивных функций.

-fcmm-elim-common-blocks
По умолчанию:

выкл., но включается флагом -O.

Включает оптимизацию удаления общих блоков в генераторе кода. Эта оптимизация пытается найти идентичные блоки Cmm и удалить их дубликаты.

-fcmm-sink
По умолчанию:

выкл., но включается флагом -O.

Включает проход перемещения вниз в генераторе кода. Эта оптимизация пытается найти идентичные блоки Cmm и удалить дубликаты; она также пытается перемещать привязки переменных ближе к местам их использования. Кроме того, она встраивает простые выражения, например литералы или регистры.

-fcmm-static-pred
По умолчанию:

выкл., но включается флагом -O.

Включает статическое предсказание потока управления для итогового кода Cmm. Если этот флаг включён, GHC применяет определённые эвристики для выявления циклов и часто выполняемых путей кода. Затем эта информация используется при распределении регистров и компоновке кода.

-fcmm-control-flow
По умолчанию:

выкл., но включается флагом -O.

Включает некоторые оптимизации потока управления в генераторе кода Cmm: объединение базовых блоков и устранение переходов, следующих непосредственно за другими переходами.

-fasm-shortcutting
По умолчанию:

выкл., но включается флагом -O2.

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

В основном это выполняется во время проходов Cmm. Однако при этом могут быть пропущены пограничные случаи. Поэтому при -O2 этот флаг повторно запускает проход на этапе генерации ассемблерного кода, чтобы обработать их. Обратите внимание, что из-за ограничений платформы (#21972) этот флаг ничего не делает в macOS.

-fblock-layout-cfg
По умолчанию:

выкл., но включается флагом -O.

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

Ожидается, что в среднем это повысит производительность, но фактический эффект может различаться.

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

-fblock-layout-weights

Этот флаг предназначен для опытных пользователей. Его основная цель — упростить отладку и настройку нового алгоритма компоновки кода. Нет гарантии, что значения, дающие лучшие результаты сейчас, не ухудшат их в следующем выпуске.

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

-fblock-layout-weightless
По умолчанию:

выкл.

Если не используется компоновка блоков на основе CFG, порядок блоков определяется либо последним переходом в базовом блоке, либо самым весомым исходящим ребром блока в CFG.

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

Если этот флаг включён, а -fblock-layout-cfg отключён, компоновка блоков работает так же, как в версии 8.6 и более ранних.

-fcpr-anal
По умолчанию:

выкл., но включается флагом -O.

Включает CPR-анализ, который задействует преобразование worker/wrapper (см. -fworker-wrapper), чтобы распаковать результат функции, например

sum :: [Int] -> Int
sum []     = 0
sum (x:xs) = x + sum xs

CPR-анализ обнаружит, что каждый путь выполнения создаёт сконструированный продукт, например I# 0# в первой ветви (где GHC.Exts.I# — конструктор данных для Int, упаковывающий примитивный целочисленный литерал 0# типа Int#), и оптимизирует выражение до

sum xs = I# ($wsum xs)
$wsum []        = 0#
$wsum (I# x:xs) = x# +# $wsum xs

после чего sum может быть встроена, потенциально устранив упаковку I#.

Вот пример функции, которая не возвращает сконструированный продукт:

f :: [Int] -> (Int -> Int) -> Int
f []     g = g 0
f (x:xs) g = x + f xs g

Выражение g 0 не является сконструированным продуктом, поскольку нам ничего не известно о g.

CPR-анализ работает и вложенно, например:

sumIO :: [Int] -> IO Int
sumIO []     = return 0
sumIO (x:xs) = do
  r <- sumIO xs
  return $! x + r

Обратите внимание на использование $!: без него GHC не смог бы определить, что вычисление r и x завершается (и довольно быстро). В качестве альтернативы можно вычислить оба выражения с помощью строгого сопоставления с образцом или seq, однако идиома return $! <res> должна работать надёжнее и требует меньше размышлений. Приведённый выше пример будет оптимизирован до

sumIO :: [Int] -> IO Int
sumIO xs = IO $ \s -> case $wsum xs s of
  (# s', r #) -> (# s', I# r #)
$wsumIO :: [Int] -> (# State# RealWorld, Int# #)
$wsumIO []        s = (# s, 0# #)
$wsumIO (I# x:xs) s = case $wsumIO xs of
  (# s', r #) -> (# s', x +# r#)

Затем последний вариант может встроить sumIO и устранить конструктор I#. Распаковка результата действия State должна работать аналогично.

-fcse
По умолчанию:

выкл., но включается флагом -O.

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

-fstg-cse
По умолчанию:

выкл., но включается флагом -O.

Включает оптимизацию устранения общих подвыражений для промежуточного языка STG. Она позволяет объединять некоторые подвыражения, различающиеся типами, но не представлением.

-fspec-eval
По умолчанию:

вкл.

Начиная с версии:

9.14.1

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

Обратите внимание: отключение этого флага полностью отключает спекулятивное вычисление, поэтому -fspec-eval-dictfun не будет иметь эффекта.

-fspec-eval-dictfun
По умолчанию:

выкл.

Начиная с версии:

9.14.1

Включает спекулятивное (строгое) вычисление функций словарей.

Проще всего объяснить это на примере:

instance C a => D a where ...

g :: D a => a -> Int
g x = ...

f :: C a => a -> Int
f x = g x

Функция f должна передать словарь D a функции g и использует функцию словаря C a => D a для его вычисления. Если включено спекулятивное вычисление функций словарей, этот словарь вычисляется строго.

Спекулятивное вычисление функций словарей может немного повысить производительность, поскольку позволяет избежать создания thunk. Однако оно приводит к ненужным вычислениям и выделению памяти, если словарь не используется. Если словарь велик, объём выделяемой памяти значительно увеличивается. См. (#25284).

-fdicts-cheap
По умолчанию:

выкл.

Очень экспериментальный флаг, который заставляет оптимизатор считать выражения со значениями-словарями дешёвыми.

-fdicts-strict
По умолчанию:

выкл., но включается флагом -O2.

Делает словари строгими.

Это позволяет срабатывать WW для ограничений-словарей, что обычно повышает производительность во время выполнения. В редких случаях из-за изменения поведения встраивания это может значительно увеличить время компиляции. Изредка это также может отрицательно сказаться на производительности во время выполнения.

Если включение этого флага приводит к ухудшению показателей, попробуйте немного повысить порог встраивания с помощью -funfolding-use-threshold=⟨n⟩ (примерно на 30), так как, вероятно, причина в известном ограничении, описанном в #18421.

-fdmd-tx-dict-sel
По умолчанию:

вкл.

Использует специальный преобразователь требований для селекторов словарей. Начиная с версии 9.2 это поведение включено безусловно.

-fdo-eta-reduction
По умолчанию:

вкл.

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

-fdo-lambda-eta-expansion
По умолчанию:

вкл.

Выполняет эта-расширение let-привязок, увеличивая их арность.

-fdo-clever-arg-eta-expansion
По умолчанию:

выкл.

Выполняет эта-расширение аргументов, увеличивая их арность, чтобы избежать ненужного выделения памяти для thunk.

-feager-blackholing
По умолчанию:

выкл.

Обычно GHC помечает thunk как black hole только при переключении потоков. Этот флаг заставляет его делать это сразу при входе в thunk. См. Haskell на многопроцессорной системе с общей памятью.

Обсуждение применения см. в разделе Параметры компиляции для параллелизма SMP.

-fexcess-precision
По умолчанию:

выкл.

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

Обратите внимание, что генератор машинного кода для 32-разрядной архитектуры x86 поддерживает только режим избыточной точности, поэтому ни -fexcess-precision, ни -fno-excess-precision не оказывают никакого эффекта. Это известная ошибка; см. Ошибки в GHC.

-fexpose-all-unfoldings
По умолчанию:

выкл.

Флаг, раскрывающий все развёртки, в том числе для очень больших или рекурсивных функций.

Однако GHC по-прежнему использует обычные эвристики при выборе функций для встраивания и специализации. Поэтому для получения преимуществ в местах использования нужны дополнительные меры. Обычно это один из следующих вариантов:

  • -fspecialise-aggressively, чтобы принудительно выполнить максимально возможную специализацию.
  • Прагмы {-# SPECIALIZE #-}, гарантирующие специализацию для определённых типов.
  • Использование специальной функции inline для принудительного встраивания.
-fexpose-overloaded-unfoldings
По умолчанию:

выкл.

Этот экспериментальный флаг — несколько менее затратная альтернатива флагу -fexpose-all-unfoldings.

Вместо раскрытия всех функций он раскрывает только те, в типах которых есть ограничения. Флаг предназначен для случаев, когда специализация считается критически важной, но -fexpose-all-unfoldings слишком сильно увеличивает время компиляции.

Это не гарантирует раскрытия всех функций, которым специализация может быть полезна. Например, в настоящее время не раскрываются развёртки, в которых класс типов скрыт под newtype.

Например, в случае:

newtype NT a = NT (Integral a => a)

foo :: NT a -> T1 -> TR

GHC не будет искать ограничения внутри NT, поэтому foo не будет распознана как подлежащая специализации, и этот флаг не раскроет её развёртку, даже если включён -fexpose-overloaded-unfoldings.

Все остальные оговорки относительно -fexpose-overloaded-unfoldings также остаются в силе; подробности см. там.

-ffloat-in
По умолчанию:

выкл., но включается флагом -O.

Перемещает let-привязки внутрь, ближе к месту их связывания. См. Вынос let: перемещение привязок для ускорения программ (ICFP’96).

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

Однако эта оптимизация не всегда полезна (поэтому GHC применяет некоторые эвристики, чтобы определить, когда её использовать). Подробности сложны, но простой пример: часто полезно перемещать let-привязки наружу, чтобы сгруппировать несколько let-привязок в одну большую. Это позволяет объединить их выделение памяти и облегчить работу сборщика мусора и аллокатора.

-ffull-laziness
По умолчанию:

выкл., но включается флагом -O.

Выполняет оптимизацию полной ленивости (также известную как вынос let), перемещая let-привязки за пределы охватывающих их лямбд в надежде, что благодаря этому они будут вычисляться реже. См. Вынос let: перемещение привязок для ускорения программ (ICFP’96). Полная ленивость усиливает совместное использование, что может привести к увеличению объёма занятой памяти.

Примечание

GHC не реализует полную ленивость в полном объёме. Хотя оптимизация полной ленивости в GHC включает некоторые преобразования, которые выполнялись бы при полностью ленивой реализации (например, извлечение повторяющихся вычислений из циклов), эти преобразования применяются непоследовательно, поэтому не полагайтесь на них.

-ffun-to-thunk
По умолчанию:

выкл.

Преобразование worker/wrapper удаляет неиспользуемые аргументы, но обычно не удаляет их все, чтобы замыкание функции не превратилось в thunk, что может привести к утечке памяти и/или нарушить встраивание. Этот флаг позволяет worker/wrapper удалить все лямбды значений.

Этот флаг не действовал при включённом -ffull-laziness, поскольку тот выносил thunk из константной worker-функции, даже если -ffun-to-thunk был отключён.

Поэтому использование этого флага признано устаревшим начиная с GHC 9.4.1. Вместо него рекомендуется передавать -fno-full-laziness. Это означает, что при наличии -fno-full-laziness worker/wrapper не сможет превратить функцию в thunk. Если это неудобно, оставьте комментарий в системе отслеживания ошибок (#21204).

-finter-module-far-jumps
По умолчанию:

Выкл.

Этот флаг заставляет GHC использовать дальние переходы вместо ближних для всех переходов между модулями. Это устраняет необходимость в островках переходов и исправлениях переходов компоновщиком, с которыми некоторые компоновщики не справляются. (#24648)

Это приводит к очень небольшому увеличению размера кода (примерно на 2%) и времени выполнения (примерно на 0,6%).

Обратите внимание, что в настоящее время этот флаг влияет только на бэкенд NCG для AArch64.

-fignore-asserts
По умолчанию:

выкл., но включается флагом -O.

Заставляет GHC игнорировать вызовы функции Exception.assert в исходном коде (иными словами, заменяет Exception.assert p e на e; см. Утверждения).

-fignore-interface-pragmas
По умолчанию:

Подразумевается флагом -O0; в противном случае выключен.

Указывает GHC игнорировать всю необязательную информацию при чтении файлов интерфейсов. То есть, даже если M.hi содержит информацию о развёртке или строгости для функции, GHC проигнорирует её.

-fkeep-auto-rules
По умолчанию:

выкл.

Начиная с версии:

9.10.1

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

Это поведение можно изменить с помощью -fkeep-auto-rules. При включении флага сохраняются все автоматически созданные правила.

-flate-dmd-anal
По умолчанию:

выкл.

Повторно запускает анализ требований в конце конвейера упрощения. Мы обнаружили возможности выявить строгость, которые были незаметны на более ранних этапах; кроме того, оптимизации, такие как -fspec-constr, могут создавать функции с неиспользуемыми аргументами, которые удаляются при позднем анализе требований. Улучшения невелики, но затраты также невелики. См. заметки на странице вики.

-fliberate-case
По умолчанию:

выкл., но включается флагом -O2.

Включает преобразование liberate-case. Оно однократно разворачивает рекурсивную функцию в её собственном RHS, чтобы избежать повторного анализа case для свободных переменных. Это немного похоже на специализацию по шаблонам вызовов (-fspec-constr), но применяется к свободным переменным, а не к аргументам.

-fliberate-case-threshold=⟨n⟩
По умолчанию:

2000

Задаёт порог размера для преобразования liberate-case.

-floopification
По умолчанию:

выкл., но включается флагом -O.

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

-fllvm-pass-vectors-in-regs
По умолчанию:

вкл.

Начиная с GHC 8.8 этот флаг не действует: соответствующее поведение всегда включено. Ранее он указывал GHC использовать нативные векторные регистры платформы для передачи векторных аргументов при вызовах функций.

-fmax-inline-alloc-size=⟨n⟩
По умолчанию:

128

Задаёт максимальный размер встроенных выделений массива в n байт. GHC выделит неприкреплённые массивы со статически известным размером в текущем блоке nursery, если их размер не превышает n байт, не учитывая переполнение кучи при сборке мусора. Это значение должно быть значительно меньше размера блока (обычно: 4096).

-fmax-inline-memcpy-insns=⟨n⟩
По умолчанию:

32

Встраивает вызовы memcpy, если при этом генерируется не более ⟨n⟩ псевдоинструкций.

-fmax-inline-memset-insns=⟨n⟩
По умолчанию:

32

Встраивает вызовы memset, если при этом генерируется не более n псевдоинструкций.

-fmax-relevant-binds=⟨n⟩
По умолчанию:

6

Проверка типов иногда выводит фрагмент окружения типов в сообщениях об ошибках, но только до максимального количества, задаваемого этим флагом. Отключение ограничения с помощью -fno-max-relevant-binds позволяет выводить неограниченное количество. Привязки на синтаксическом верхнем уровне обычно также исключаются (поскольку их может быть много), но -fno-max-relevant-binds включает и их.

-fmax-uncovered-patterns=⟨n⟩
По умолчанию:

4

Максимальное количество несовпавших шаблонов, отображаемых в предупреждениях, создаваемых флагами -Wincomplete-patterns и -Wincomplete-uni-patterns.

-fmax-simplifier-iterations=⟨n⟩
По умолчанию:

4

Задаёт максимальное количество итераций упрощателя.

-flocal-float-out
По умолчанию:

вкл.

Включает локальный вынос привязок из правой части let(rec) в упрощателе. Например:

let x = let y = rhs_y in rhs_x in blah
==>
let y = rhs_y in let x = rhs_x in blah

См. статью «Let-floating: moving bindings to give faster programs» (Вынос let: перемещение привязок для ускорения программ), Partain, Santos и Peyton Jones; ICFP 1996. https://www.microsoft.com/en-us/research/publication/let-floating-moving-bindings-to-give-faster-programs/

Примечание

Это отличается от прохода глобального выноса, который можно отключить флагом -fno-full-laziness.

-flocal-float-out-top-level
По умолчанию:

вкл.

Включает локальный вынос привязок верхнего уровня из правой части let(rec) в упрощателе. Например:

x = let y = e in (a,b) ===> y = e; x = (a,b)

См. статью «Let-floating: moving bindings to give faster programs» (Вынос let: перемещение привязок для ускорения программ), Partain, Santos и Peyton Jones; ICFP 1996. https://www.microsoft.com/en-us/research/publication/let-floating-moving-bindings-to-give-faster-programs/

Обратите внимание: если задан флаг -fno-local-float-out, он имеет приоритет.

Примечание

Это отличается от прохода глобального выноса, который можно отключить флагом -fno-full-laziness.

-fmax-worker-args=⟨n⟩
По умолчанию:

10

Функция не будет разделена на worker и wrapper, если число аргументов-значений получившейся worker-функции превышает и число аргументов исходной функции, и это значение.

-fmax-forced-spec-args=⟨n⟩
По умолчанию:

512

При использовании SPEC из GHC.Types для принудительного запуска SpecConstr для функции иногда могут создаваться функции с неправдоподобно большим числом аргументов, что приводит к значительному увеличению времени компиляции при незначительном выигрыше в производительности.

Поскольку обычно это нежелательно, мы не запускаем SpecConstr и выдаём предупреждение, если число аргументов в получившейся функции превышает значение, заданное -fmax-forced-spec-args.

-fno-opt-coercion
По умолчанию:

оптимизация coercion включена.

Отключает оптимизатор coercion.

-fno-pre-inlining
По умолчанию:

предварительное встраивание включено

Отключает предварительное встраивание.

-fno-state-hack
По умолчанию:

state hack включён

Отключает «state hack», согласно которому любая лямбда с токеном State# в качестве аргумента считается вызываемой только один раз, поэтому встраивание выражений внутрь неё считается допустимым. Это может повысить производительность кода монад IO и ST, но сопряжено с риском уменьшения совместного использования результатов вычислений.

-fomit-interface-pragmas
По умолчанию:

Подразумевается флагом -O0, иначе выключен.

Указывает GHC опустить всю необязательную информацию из файла интерфейса, создаваемого для компилируемого модуля (например, M). Это означает, что модуль, импортирующий M, увидит только типы экспортируемых M функций, но не их развёртки, сведения о строгости и т. д. Поэтому, например, ни одна экспортируемая модулем M функция не будет встроена в импортирующий модуль. Преимущество в том, что модули, импортирующие M, придётся перекомпилировать реже (только если изменятся типы экспортируемых M сущностей, а не при изменении их реализации).

-fomit-yields
По умолчанию:

вкл. (yield не вставляются)

Указывает GHC опускать проверки кучи, если выделение памяти не выполняется. Хотя это уменьшает размер двоичных файлов примерно на 5%, потоки, выполняющиеся в тесных циклах без выделения памяти, не будут своевременно вытесняться. Если важно всегда иметь возможность прервать такие потоки, отключите эту оптимизацию. Если необходимо гарантировать возможность прерывания, рассмотрите также возможность перекомпиляции всех библиотек с отключённой оптимизацией.

-fobject-determinism
По умолчанию:

детерминированность объектного кода отключена

Указывает GHC создавать полностью детерминированный объектный код. Для создания детерминированных объектных файлов требуется дополнительный проход переименования перед конвейером Cmm, который детерминированно переименовывает все Unique. Это может немного снизить скорость компиляции, но гарантирует детерминированность объектных файлов.

-fpedantic-bottoms
По умолчанию:

выкл.

Заставляет GHC точнее обрабатывать bottom (см. также -fno-state-hack). В частности, запрещает GHC выполнять eta-расширение через выражение case: это хорошо для производительности, но плохо, если вы используете seq для частичных применений.

-fregs-graph
По умолчанию:

выкл. из-за ошибки, приводящей к снижению производительности (#7679)

Применяется только совместно с нативным генератором кода. Использует распределитель регистров на основе раскраски графа для выделения регистров в нативном генераторе кода. По умолчанию GHC использует более простой и быстрый линейный распределитель регистров. Его недостаток в том, что он обычно генерирует код более низкого качества.

-fregs-iterative
По умолчанию:

выкл.

Применяется только совместно с нативным генератором кода. Использует итеративный распределитель регистров на основе раскраски графа с объединением для выделения регистров в нативном генераторе кода. Это тот же распределитель регистров, что и -fregs-graph, но он также включает итеративное объединение при выделении регистров.

-fsimplifier-phases=⟨n⟩
По умолчанию:

2

Задаёт количество фаз упрощателя. Игнорируется при использовании -O0.

-fsimpl-tick-factor=⟨n⟩
По умолчанию:

100

Оптимизатор GHC может зациклиться, если написать незавершающиеся правила переписывания (Правила переписывания) или (что менее желательно) реализовать рекурсию через типы данных (Ошибки в GHC). Чтобы компилятор не попал в бесконечный цикл, оптимизатор ведёт счётчик «тактов» и прекращает встраивание и применение правил переписывания, когда счётчик превышает заданное значение. Ограничение задаётся как множитель размера программы, поэтому для больших программ выделяется больше тактов. Флаг -fsimpl-tick-factor позволяет изменить множитель. По умолчанию он равен 100; значения больше 100 дают больше тактов, а значения меньше 100 — меньше.

Когда счётчик тактов исчерпан, GHC выводит сводку выполненных шагов упрощателя; с помощью -fddump-simpl-stats можно сформировать гораздо более подробный список. Обычно он достаточно точно указывает на цикл, поскольку некоторые значения очень велики.

-fdmd-unbox-width=⟨n⟩
По умолчанию:

3

Анализ представлений оптимистично предполагает, что у функции, возвращающей запись не более чем с -fdmd-unbox-width полями, есть только такие точки вызова, которым не нужна оболочка возвращаемой записи. Это, в свою очередь, может позволить выполнить распаковку большего числа аргументов. Установите значение 0, чтобы включить полностью консервативный режим (он гарантирует, что этот механизм не приведёт к повторной упаковке).

-fspec-constr
По умолчанию:

выкл., но включается флагом -O2.

Включает специализацию по шаблонам вызова; см. Специализация по шаблонам вызова для программ на Haskell.

Эта оптимизация специализирует рекурсивные функции в соответствии с «формой» их аргументов. Лучше всего объяснить это на примере:

last :: [a] -> a
last [] = error "last"
last (x : []) = x
last (x : xs) = last xs

В этом коде, после того как мы прошли первоначальную проверку на пустой список, мы знаем, что в рекурсивном случае это сопоставление с образцом избыточно. Поэтому -fspec-constr преобразует приведённый выше код в:

last :: [a] -> a
last []       = error "last"
last (x : xs) = last' x xs
    where
      last' x []       = x
      last' x (y : ys) = last' y ys

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

Авторы библиотек также могут указать GHC выполнять специализацию по шаблонам вызова с особой агрессивностью. Это необходимо для некоторых высокооптимизированных библиотек, где может потребоваться специализация независимо от её количества или размера кода. Например, рассмотрим упрощённый сценарий использования из библиотеки vector:

import GHC.Types (SPEC(..))

foldl :: (a -> b -> a) -> a -> Stream b -> a
{-# INLINE foldl #-}
foldl f z (Stream step s _) = foldl_loop SPEC z s
  where
    foldl_loop !sPEC z s = case step s of
                            Yield x s' -> foldl_loop sPEC (f z x) s'
                            Skip       -> foldl_loop sPEC z s'
                            Done       -> z

Здесь, после того как GHC встроит тело foldl в точку вызова, он выполнит очень агрессивную специализацию по шаблонам вызова для foldl_loop из-за использования SPEC в аргументе тела цикла. SPEC из GHC.Types распознаётся компилятором особым образом.

(Примечание: крайне важно использовать seq или строгий образец для аргумента SPEC!)

В частности, после встраивания это сделает f непосредственно доступным телу цикла, что позволит активно специализировать рекурсивные случаи.

-fspec-constr-keen
По умолчанию:

выкл.

Если этот флаг включён, специализация по шаблонам вызова будет специализировать вызов (f (Just x)) с явным аргументом-конструктором, даже если аргумент не анализируется в теле функции. Иногда это полезно; например, аргумент можно передать другой функции, которую можно специализировать.

-fspec-constr-count=⟨n⟩
По умолчанию:

3

Задаёт максимальное количество специализаций, создаваемых преобразованием SpecConstr для одной функции.

-fspec-constr-threshold=⟨n⟩
По умолчанию:

2000

Задаёт порог размера для преобразования SpecConstr.

-fspecialise
По умолчанию:

выкл., но включается флагом -O.

Специализирует каждую определённую в этом модуле функцию с перегрузкой по классу типов для типов, с которыми она вызывается в этом модуле. Если задан флаг -fcross-module-specialise, специализируются также импортированные функции с прагмой INLINABLE (прагма INLINABLE).

-fspecialise-aggressively
По умолчанию:

выкл.

Этот флаг управляет только специализацией импортированных функций. По умолчанию импортированная функция специализируется, только если она помечена как INLINEABLE или INLINE. Но при использовании -fspecialise-aggressively импортированная функция специализируется, если её развёртка доступна в файле интерфейса. (Используйте -fexpose-all-unfoldings или -fexpose-overloaded-unfoldings, чтобы развёртка попала в файл интерфейса.)

-fspecialise-aggressively не входит ни в один уровень оптимизации, поскольку может значительно увеличить размер кода.

-fcross-module-specialise
По умолчанию:

выкл., но включается флагом -O.

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

-fpolymorphic-specialisation
По умолчанию:

выкл.

Внимание: эта возможность носит экспериментальный характер и может привести к некорректным результатам во время выполнения. Используйте на свой страх и риск (#23469, #23109, #21229, #23445).

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

-flate-specialise
По умолчанию:

выкл.

Запускает ещё один проход специализации ближе к концу конвейера оптимизации. Это позволяет обнаружить возможности специализации, появившиеся после предыдущего прохода специализации или другого встраивания.

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

-fspecialise-incoherents
По умолчанию:

вкл.

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

-finline-generics
По умолчанию:

выкл., но включается флагом -O.

Начиная с версии:

9.2.1

Добавляет к методам производных экземпляров Generic и Generic1 прагмы INLINE[1], используя эвристики, зависящие от размера соответствующего типа данных. Повышает производительность алгоритмов на основе Generics, поскольку GHC чаще удаётся устранить промежуточное представление.

-finline-generics-aggressively
По умолчанию:

выкл.

Начиная с версии:

9.2.1

Добавляет прагмы INLINE[1] ко всем методам производных экземпляров Generic и Generic1.

Этот флаг следует использовать только в модулях с производными экземплярами Generic, которые эвристики флага -finline-generics сочли неподходящими для аннотаций INLINE[1], хотя вы знаете, что такие аннотации будут полезны.

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

-fsolve-constant-dicts
По умолчанию:

выкл., но включается флагом -O.

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

Например:

class M a b where m :: a -> b

type C a b = (Num a, M a b)

f :: C Int b => b -> Int -> Int
f _ x = x + 1

Для тела f требуется экземпляр Num Int. Это ограничение можно разрешить из контекста, поскольку у нас есть C Int b, который даёт решение для Num Int. Однако часто можно получить гораздо более эффективный код, напрямую разрешив ограничение с помощью доступного словаря Num Int. Это устраняет потенциально множество уровней косвенности и, что особенно важно, позволяет сработать другим оптимизациям, поскольку словарь будет известен статически, а функции-селекторы можно будет встроить.

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

-fstatic-argument-transformation
По умолчанию:

выкл.

Включает преобразование статических аргументов, которое превращает рекурсивную функцию в нерекурсивную с локальным рекурсивным циклом. См. главу 7 диссертации Andre Santos.

-fstg-lift-lams
По умолчанию:

выкл., но включается флагом -O2.

Включает позднюю оптимизацию подъёма лямбд в промежуточном языке STG. Она выборочно поднимает локальные функции на верхний уровень, превращая свободные переменные в параметры функции.

-fstg-lift-lams-known
По умолчанию:

выкл.

Разрешает превращать известные вызовы в неизвестные при позднем подъёме лямбд. Это считается невыгодным, поэтому по умолчанию отключено.

-fstg-lift-lams-non-rec-args
По умолчанию:

5

При позднем подъёме лямбд создаёт функции верхнего уровня без рекурсии, содержащие не более <n> параметров. По умолчанию используется значение 5 — количество доступных регистров параметров на x86_64.

-fstg-lift-lams-rec-args
По умолчанию:

5

При позднем подъёме лямбд создаёт рекурсивные функции верхнего уровня, содержащие не более <n> параметров. По умолчанию используется значение 5 — количество доступных регистров параметров на x86_64.

-fstrictness
По умолчанию:

выключено, но включается параметром -O.

Включить анализ востребованности.

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

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

Преобразование worker/wrapper (-fworker-wrapper) отвечает за использование возможностей снятия упаковки и замену отсутствующих аргументов фиктивными значениями. Для аргументов, которые нельзя представить без упаковки, возможности вычисления по значению и по имени используются в CorePrep при преобразовании в STG.

Интересно знать не только, как часто связывание вычисляется, но и как часто вызывается функция. Если функция вызывается не более одного раза, мы можем свободно расширить её по эта, даже если это уничтожит совместно используемую работу в случае многократного вызова функции. Эта информация преобразуется в аннотации OneShotInfo, на которые реагирует Simplifier.

Обозначения

Таким образом, анализ востребованности заключается в консервативном выводе нижних и верхних границ количества вычислений или вызовов. Часть «сколько раз» мы называем кратностью. В компиляторе и отладочном выводе мы различаем следующие интервалы кратности, используемые для её приближённого представления:

Интервал

Множество обозначаемых кратностей

Синтаксис

Объяснение связи синтаксиса с семантикой

[1,0]

{}

B

Нижний элемент

[0,0]

{0}

A

Отсутствует

[0,1]

{0,1}

M

Используется не более одного раза («возможно»)

[0,ω]

{0,1,ω}

L

Ленивое. Верхний элемент, отсутствие информации; используется не менее 0 и не более многих раз

[1,1]

{1}

1

Строгое, используется ровно один раз

[1,ω]

{1,ω}

S

Строгое, возможно, используется много раз

Заметьте, что нет смысла различать кратность 2 и 3 или даже 4232123. Мы просто приближённо представляем случаи >1 значением ω, означающим «много раз».

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

Полный синтаксис кратностей, требований и подтребований в нотации BNF:

card ::= B | A | M | L | 1 | S    semantics as in the table above

d    ::= card sd                  card = how often, sd = how deep
      |  card                     abbreviation: Same as "card card"

sd   ::= card                     polymorphic sub-demand, card at every level
      |  P(d,d,..)                product sub-demand
      |  C(card,sd)               call sub-demand

Например, fst является строгой по своему аргументу, а также по первому компоненту аргумента. Второй компонент аргумента она не вычисляет. Это выражается требованием 1P(1L,A). P означает «подтребование к продукту», в котором для каждого поля продукта задано требование. Обозначение 1L просто означает «вычисляется строго (1), а всё вложенное вычисляется согласно L» — например, без информации, поскольку это зависело бы от контекста вычисления в месте вызова fst. Роль L в 1L — это роль полиморфного подтребования, семантически эквивалентного подтребованию P(LP(..)), которое мы просто сокращаем до обозначения кратности L (и, следовательно, перегружаем).

Рассмотрим другой пример: выражение x + 1 вычисляет x согласно требованию 1P(L). Мы уже видели, что отдельные буквы обозначают кратности и полиморфные подтребования, но что означает отдельная буква L для требования? Такое требование из одной буквы просто раскрывается в кратность и полиморфное подтребование с той же буквой: например, L эквивалентно LL после раскрытия требования из одной буквы; оно, в свою очередь, эквивалентно LP(LP(..)), и так далее — L. Из контекста всегда ясно, идёт ли речь о кратности, подтребовании или требовании.

Сигнатуры требований

Свойства востребованности функции обобщаются в её сигнатуре требований. Общий синтаксис:

{x->dx,y->dy,z->dz...}<d1><d2><d3>...<dn>div
        ^              ^   ^   ^      ^   ^
        |              |   |   |      |   |
        |              \---+---+------/   |
        |                  |              |
   demand on free        demand on      divergence
     variables           arguments      information
 (omitted if empty)                     (omitted if
                                      no information)

Свойства востребованности fst обобщаются в её сигнатуре требований <1P(1L,A)>, которая означает: «Если fst применяется к одному аргументу, этот аргумент вычисляется согласно 1P(1L,A)». Другой пример: сигнатура требований seq была бы <1A><1L>, а сигнатура требований + — <1P(L)><1P(L)>.

Если сведения о расходимости не опущены, они могут быть b (гарантированно расходится) или x (гарантированно расходится или выбрасывает точное исключение). Например, error имеет сигнатуру требований <S>b, а throwIO (единственный способ выбросить точное исключение) имеет сигнатуру требований <_><L><L>x (без учёта сложного требования к словарю Exception).

Подтребования вызова

Рассмотрим maybe:

maybe :: b -> (a -> b) -> Maybe a -> b
maybe n _ Nothing  = n
maybe _ s (Just a) = s a

Его сигнатура требований — <L><MC(M,L)><1L>. C(M,L) — это подтребование вызова, означающее «вызывается не более одного раза, причём результат используется согласно L». Выражение f `seq` f 1 помещает f под требование SC(1,L) и служит примером того, что верхняя граница кратности вычисления не совпадает с верхней границей кратности вызова.

Кратность всегда рассматривается относительно кратности вызова внешнего выражения, поэтому g 1 2 + g 3 4 помещает g под требование SC(S,C(1,L)), означающее: «вызывается несколько раз (S), но при каждом вызове с одним аргументом применяется ровно один раз к другому аргументу (1)».

-fstrictness-before=⟨n⟩

Выполнить дополнительный анализ востребованности перед фазой ⟨n⟩ упрощения.

-funbox-small-strict-fields
По умолчанию:

выключено, но включается параметром -O.

Этот параметр приводит к распаковке, если это возможно, всех полей конструкторов, помеченных как строгие (то есть «!»), размер представления которых меньше или равен размеру указателя. Это эквивалентно добавлению прагмы UNPACK (см. прагма UNPACK) к каждому строгому полю конструктора, удовлетворяющему ограничению по размеру.

Например, поля конструкторов в следующих типах данных

data A = A !Int
data B = B !A
newtype C = C B
data D = D !C

при включённом -funbox-small-strict-fields будут представлены значением Int# (см. Типы без упаковки и примитивные операции).

Этот параметр действует менее грубо, чем -funbox-strict-fields: он редко может ухудшить результат. Если вы используете -funbox-small-strict-fields для включения снятия упаковки по умолчанию, его можно отключить для определённых полей конструкторов с помощью прагмы NOUNPACK (см. прагма NOUNPACK).

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

Также обратите внимание: в модулях, объявляющих экземпляры семейств типов или данных, GHC не будет автоматически использовать эти экземпляры для распаковки полей других объявлений в том же модуле. Чтобы обойти эту проблему, рекомендуется использовать явные прагмы UNPACK.

-funbox-strict-fields
По умолчанию:

выключено

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

Этот параметр действует довольно грубо: иногда он может ухудшить результат. Возможно, лучше выборочно снимать упаковку с полей с помощью прагм UNPACK. Другой вариант — использовать -funbox-strict-fields, чтобы включить снятие упаковки по умолчанию, но отключить его для определённых полей конструкторов с помощью прагмы NOUNPACK (см. прагма NOUNPACK).

Также можно использовать -funbox-small-strict-fields, чтобы снимать упаковку только с «маленьких» строгих полей.

Примечание: в модулях, объявляющих экземпляры семейств типов или данных, GHC не будет автоматически использовать эти экземпляры для распаковки полей других объявлений в том же модуле. Чтобы обойти эту проблему, рекомендуется использовать явные прагмы UNPACK.

-funfolding-creation-threshold=⟨n⟩
По умолчанию:

750

Определяет максимальный размер развёртки функции, допустимый для GHC. (Развёртке присваивается «размер», отражающий затраты на «разрастание кода» при раскрытии этой развёртки (то есть встраивании) в месте вызова. Большей функции присваивается более высокая стоимость.)

Последствия:

  1. ничто, превышающее это значение, не будет встроено (если только оно не помечено прагмой INLINE)
  2. ничто, превышающее это значение, не будет помещено в файл интерфейса.

Увеличение этого значения скорее приведёт к увеличению времени компиляции, чем к ускорению кода. Параметр -funfolding-use-threshold=⟨n⟩ полезнее.

-funfolding-dict-discount=⟨n⟩
По умолчанию:

30

Насколько охотно компилятор должен встраивать словари?

-funfolding-fun-discount=⟨n⟩
По умолчанию:

60

Насколько охотно компилятор должен встраивать функции?

-funfolding-keeness-factor=⟨n⟩

Этот коэффициент устарел начиная с GHC 9.0.1. Подробности см. в #15304. Пользователям, которым нужно управлять встраиванием, рекомендуется рассмотреть параметр -funfolding-use-threshold=⟨n⟩.

-funfolding-use-threshold=⟨n⟩
По умолчанию:

80

Это магический пороговый размер развёртки (то есть встраивания): если размер меньше этого значения, определение функции будет развёрнуто в месте вызова; если больше — нет. Вычисляемый размер функции зависит от двух факторов: фактического размера выражения за вычетом скидок, применимых с учётом контекста, в который будет встроено выражение.

Отличие этого параметра от -funfolding-creation-threshold=⟨n⟩ состоит в том, что он определяет, будет ли определение функции встроено в месте вызова. Другой параметр определяет, будет ли определение функции вообще сохранено для возможного встраивания.

-funfolding-case-threshold=⟨n⟩
По умолчанию:

2

В целом GHC довольно охотно встраивает небольшие функции. Однако иногда после встраивания они дополнительно расширяются дальнейшим встраиванием, поскольку теперь применяются к «интересным» аргументам. Ещё хуже то, что их расширенная форма может снова ссылаться на небольшую функцию, которая затем будет встроена и расширена. Это может повторяться много раз и приводить к взрывному росту программ.

Так произошло в #18730.

Начиная с GHC 9.0 мы будем менее охотно встраивать функции на большую глубину во вложенные ветвления case. Для этого мы применяем штраф за встраивание, который увеличивается с глубиной вложенности. Однако иногда допустимо ожидать определённую (возможно, довольно большую) глубину вложенности.

В таких случаях этот параметр позволяет игнорировать первые ⟨n⟩ уровней вложенности при вычислении штрафа.

Этот параметр в сочетании с -funfolding-case-scaling=⟨n⟩ можно использовать для разрыва циклов встраивания, не отключая встраивание полностью. Для этого лучше подходит меньшее значение, хотя часто достаточно изменить коэффициент масштабирования, что и предпочтительнее.

-funfolding-case-scaling=⟨n⟩
По умолчанию:

30

В целом GHC довольно охотно встраивает небольшие функции. Однако иногда после встраивания они дополнительно расширяются дальнейшим встраиванием, поскольку теперь применяются к «интересным» аргументам. Ещё хуже то, что их расширенная форма может снова ссылаться на небольшую функцию, которая затем будет встроена и расширена. Это может повторяться много раз и приводить к взрывному росту программ.

Так произошло в #18730.

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

В таких случаях этот параметр позволяет настроить величину штрафа за встраивание во вложенные ветвления case, превышающие порог, заданный параметром -funfolding-case-threshold=⟨n⟩. При подсчёте уровня вложенности учитываются только ветвления case с более чем одним вариантом.

Для масштабирования штрафа используется 1/n. То есть чем выше значение, тем меньше штраф.

Этот параметр можно использовать для разрыва циклов встраивания. Для этой цели рекомендуется меньшее значение. Значения в диапазоне 10 <= n <= 20 позволяют выполнять некоторое встраивание, сохраняя при этом возможность компиляции GHC модулей, содержащих такие циклы.

-fworker-wrapper
По умолчанию:

выключено, но включается параметром -O.

Включить преобразование worker/wrapper после прохода анализа востребованности.

Использует сведения о строгости и отсутствии, снимая упаковку со строгих аргументов и заменяя отсутствующие поля фиктивными значениями в функции-обёртке, которая будет встроена во всех соответствующих случаях и тем самым позволит использовать специализированное соглашение о вызовах функции worker с аргументами без упаковки.

Включается параметром -O и параметром -fstrictness. Отключается параметром -fno-strictness. Включение -fworker-wrapper при отключённом анализе востребованности (параметром -fno-strictness) не имеет эффекта.

-fworker-wrapper-cbv

Отключение этого параметра предотвращает разделение W/W, если единственная выгода заключается в вычислении некоторых аргументов по значению.

В противном случае этот параметр использует сведения о строгости, передавая строгие аргументы-значения функции worker по значению. Это относится даже к функциям, для которых worker иначе не был бы создан.

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

Это может немного увеличить размер кода. Также гораздо больше функций будут разделены на worker/wrapper, что может плохо сочетаться с правилами (см. #20364), поэтому сейчас по умолчанию этот параметр отключён. В частности, если вы полагаетесь на срабатывание правил для функций, помеченных NOINLINE, но не помечаете места использования этих функций как INLINE или INLINEABLE, всё перестанет работать, если этот параметр не отключить.

Если WorkerWrapper отключён, этот параметр не оказывает эффекта.

-fbinary-blob-threshold=⟨n⟩
По умолчанию:

500000

Генератор машинного кода может либо записывать двоичные блоки (например, строковые литералы) в файл ассемблера (используя директивы ассемблера «.asciz» или «.string»), либо сохранять их как двоичные данные во временный файл, который затем подключается ассемблером (с помощью директивы ассемблера «.incbin»).

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

-fwrite-if-compression=⟨n⟩
По умолчанию:

2

Начиная с версии:

9.12.1

Этот параметр задаёт уровень сжатия файлов интерфейсов при записи на диск. Чем выше значение параметра, тем сильнее дедупликация файла интерфейса, но тем больше время компиляции. Дедупликация (при использовании режима --make и режима --interactive) уменьшает размер файлов интерфейсов, а также общее потребление памяти GHC.

Полностью отключить сжатие нельзя: GHC всегда сжимает файлы интерфейсов в некоторой степени. В настоящее время поддерживаются значения 1, 2 и 3. Меньшие или большие значения ограничиваются значениями 1 и 3 соответственно.

  • 1: минимальное сжатие. Не влияет на время выполнения, но увеличивает размер файла интерфейса и потребление памяти.
  • 2: сжатие с минимальными накладными расходами во время выполнения, уменьшающее размер файла интерфейса и потребление памяти.
  • 3: применение всех возможных методов сжатия для минимизации размера файлов интерфейсов и потребления памяти за счёт накладных расходов во время выполнения.

© 2002–2007 The University Court of the University of Glasgow. All rights reserved.
Licensed under the Glasgow Haskell Compiler License.
https://downloads.haskell.org/~ghc/9.14.1/docs/users_guide/using-optimisation.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API