Подтвердить что ты не робот

Нужно ли мне беспокоиться с праймами INLINE/INLINABLE для небольших, экспортируемых, функций, или GHC сделает это для меня?

У меня есть модуль, который собирает и экспортирует кучу небольших функций, таких как:

fromEither :: (MonadError e m) => Either e a -> m a
fromEither = either throwError return

или

withError :: MonadError e m => (e -> e) -> m a -> m a
withError = flip catchError (throwError . f)

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

В документации GHC говорится:

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

Означает ли это, что такие функции, скорее всего, будут обрабатываться так, как если бы они имели прагму INLINE (т.е. их неоптимизированный RHS, записанный в файле интерфейса)? Или мне нужно добавить INLINE себя? Добавляет ли это что-либо (что, если GHC решает, что они "достаточно малы" )?

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

4b9b3361

Ответ 1

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

Вы можете проверить, решил ли GHC сделать это, выполнив ghc --show-iface в результирующем .hi -File. Если он говорит что-то о Unfolding, как в примере ниже, при использовании этого модуля функция, скорее всего, будет встроена:

$ ghc --show-iface /usr/lib/ghc/base-4.6.0.1/Data/Either.hi
Magic: Wanted 33214052,
       got    33214052
...
38da29044ff77a85b08cebca1fed11ad
  either :: forall a c b.
            (a -> c) -> (b -> c) -> Data.Either.Either a b -> c
    {- Arity: 3, HasNoCafRefs, Strictness: LLS,
       Unfolding: (\ @ a
                     @ c
                     @ b
                     f :: a -> c
                     ds :: b -> c
                     ds1 :: Data.Either.Either a b ->
                   case ds1 of wild {
                     Data.Either.Left x -> f x Data.Either.Right y -> ds y }) -}

Ответ 2

Я хочу добавить к Joachim ответ, что количество функций в модуле также влияет на то, будет ли экспортировать их ghc. Например, Pipes.Prelude из pipes будет показывать замедление по нескольким функциям, когда я начал добавлять несколько несвязанных функций в один и тот же модуль. Добавление INLINABLE прагм к этим функциям восстановило первоначальную скорость.