Трудно понять поведение выделения памяти Haskell

я наткнулся на Хаскелла и Ф. П. и был ошеломлен возможностями. И старый ботаник математики внутри меня не имел проблем с написанием наивного кода для реальных полезных целей. Однако, несмотря на все чтение, мне все еще очень трудно понять, как не попасть в некоторые удивительные узкие места производительности.

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

m = 3
n = 3000
main = putStr $ "Soldier #" ++ (show $ whosLeft [1..n]) ++ " survived...n"

whosLeft [lucky] = lucky
whosLeft soldiers = whosLeft $ take (length soldiers -1) $ drop m $ cycle soldiers

последнее бежит в Госпоже 190, с урожайностью 63% согласно RTS.

тогда первое, что я хотел попробовать, это удалить (length soldier -1) и заменить его уменьшающимся целым числом.

время выполнения прыгнуло до госпожи 900 и урожайность вниз до 16%, и использует в 47 раз больше памяти, чем более простой код выше ! Поэтому я добавил строгую оценку, заставил тип Int, попробовал такие вещи, как удаление глобальных переменных и других, но не очень. И я просто не могу понять это замедление.

m = 3::Int
n = 3000::Int
main = putStr $ "Soldier #" ++ (show $ whosLeft n [1..n]) ++ " survived...n"

whosLeft 1 [lucky] = lucky
whosLeft n' soldiers = n' `seq` left `seq` whosLeft (n'-1) left
                where left = take (n'-1) $ drop m $ cycle soldiers

Я просеял связанные с производительностью статьи и сообщения, но я просто не вижу намека на это. Все еще будучи Haskell noob, я, должно быть, пропустил что-то большое... Как может этот один добавленный параметр (pre-chewed вычисление...) так сильно снизить скорость ?

1 ответов


первое решение заставляет весь позвоночник списка солдат, принимая его длину. Второе решение только заставляет (используя seq) the глава из списка солдат. Заменить left между seqс length left и вы получите свое представление обратно.