Как определить, будет ли Haskell кэшировать результат или пересчитывать его? - PullRequest
33 голосов
/ 17 февраля 2011

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

  1. Почему это происходит? Это функция GHCI или как?
  2. Могу ли я рассчитывать на это (то есть: могу ли я определенно узнать, будет ли кэшировано значение функции)?
  3. Могу ли я включить или отключить эту функцию для некоторых вызовов функций?

В соответствии с требованиями комментариев, вот пример, который я нашел в Интернете:

isPrime a = isPrimeHelper a primes
isPrimeHelper a (p:ps)
    | p*p > a = True
    | a `mod` p == 0 = False
    | otherwise = isPrimeHelper a ps
primes = 2 : filter isPrime [3,5..]

Я ожидал, что перед запуском он будет довольно медленным, поскольку он продолжает получать доступ к элементам primes без явного их кэширования (таким образом, если эти значения где-то не кэшированы, их нужно будет пересчитывать много раз). Но я был неправ.

Если я установлю +s в GHCI (для печати статистики времени / памяти после каждой оценки) и дважды вычислю выражение primes!!10000, это то, что я получаю:

*Main> :set +s
*Main> primes!!10000
104743
(2.10 secs, 169800904 bytes)
*Main> primes!!10000
104743
(0.00 secs, 0 bytes)

Это означает, что по крайней мере primes !! 10000 (или лучше: весь список primes, так как primes!!9999 не займет много времени) должен быть кэширован.

1 Ответ

31 голосов
/ 17 февраля 2011

primes в вашем коде - это не функция, а константа в haskellspeak, известная как CAF .Если бы он принял параметр (скажем, ()), вы бы вернули две разные версии одного и того же списка, если вызывали его дважды, но, поскольку это CAF, вы оба раза возвращали один и тот же список;

Как определение верхнего уровня ghci, primes никогда не становится недоступным, поэтому заголовок списка, на который он указывает (и, следовательно, его хвост / остальная часть вычисления), никогда не собирается сборщиком мусора.Добавление параметра предотвращает сохранение этой ссылки, тогда список будет собираться мусором, так как (!!) выполняет итерацию по нему, чтобы найти нужный элемент, а ваш второй вызов (!!) вызовет повторение всего вычисления вместо простого обхода ужевычисляемый список.

Обратите внимание, что в скомпилированных программах отсутствует область верхнего уровня, как в ghci, и вещи собирают мусор, когда последняя ссылка на них исчезает, вполне вероятно, до выхода всей программы, CAF или нетЭто означает, что ваш первый вызов займет много времени, второй - нет, и после этого, «будущее вашей программы» больше не ссылается на CAF, память, которую занимает CAF, используется повторно.

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

Если вы хотите по-настоящему разобраться в этом, я рекомендую прочитать STGбумага .Он не включает в себя новые разработки в GHC, но делает большую работу по объяснению того, как Haskell отображает на сборку, и, как следствие, жесткости, как правило, пожираются строгостью.

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