Аномалия производительности Runhaskell - PullRequest
8 голосов
/ 17 февраля 2012

Я пытаюсь понять аномалию производительности, наблюдаемую при запуске программы под 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 ]

9 голосов
/ 17 февраля 2012

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

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

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

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

5 голосов
/ 17 февраля 2012

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...