Аномалия производительности Runhaskell

Я пытаюсь понять аномалию производительности, наблюдаемую при запуске программы под runhaskell,

Рассматриваемая программа:

isFactor n = (0 ==) . (mod n)
factors x = filter (isFactor x) [2..x]
main = putStrLn $ show $ sum $ factors 10000000

Когда я запускаю это, это занимает 1,18 секунды.

Тем не менее, если я переопределить isFactor как:

isFactor n f = (0 ==) (mod n f)

тогда программа занимает 17,7 секунды.

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

Примечание. Этого не происходит при компиляции в GHC.

2 ответа

Решение

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

Даже минимальной оптимизации достаточно для того, чтобы GHC видел это и создавал идентичный код для обоих, однако, если вы компилируете с -O0 -ddump-simpl вы можете определить, что без оптимизации это имеет значение (по крайней мере, для ghc-7.2.1, YMMV с другими версиями).

С первым isFactorGHC создает одну функцию, которая передается в качестве предиката в "GHC.List.Filter", с вызовами mod 10000000 а также (==) встраиваемый. Что касается второго определения, то вместо этого происходит то, что большинство вызовов в пределах isFactor являются связанными по буквам ссылками на функции класса и не разделяются между несколькими вызовами isFactor, Так что есть много словарных накладных расходов, которые совершенно не нужны.

Это почти никогда не проблема, потому что даже настройки компилятора по умолчанию будут оптимизировать его, однако runhaskell, по-видимому, даже не делает этого. Тем не менее, я иногда структурировал код как someFun x y = \z -> потому что я знаю это someFun будет применяться частично, и это был единственный способ сохранить совместное использование вызовов (т. е. оптимизатор GHC не был достаточно умным).

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

Насколько я понимаю, если существует скомпилированная версия кода, то runhaskell буду использовать это. Поэтому, если для вас важна производительность, просто убедитесь, что вы компилируете с включенной оптимизацией. (Я думаю, что вы могли бы даже передать переключатели runhaskell чтобы включить оптимизацию - вам нужно проверить документацию...)

Другие вопросы по тегам