Память взрывается для строгой суммы / строгой складки в ghci

Как упоминалось в Почему (sum $ takeWhile (‹10000000 ) [1..]) использовать так много памяти? следующее не взрывает память в ghci :

foldl' (+) 0 $ takeWhile (< 10000000) [1 .. ]

Однако, если я создам файл, содержащий:

import Data.List

longList::[Int]
longList = [1 .. ]

result :: Int
result = foldl' (+) 0 $ takeWhile (< 10000000) longList

main = do
  print $ result

и загрузить в ghci, то при запуске программы потребление памяти резко возрастает. Почему это, и что я могу сделать, чтобы исправить программу? Я использую ghc 7.8.3.

[РЕДАКТИРОВАТЬ]

Он не кажется взрывным, если я сначала скомпилирую через ghc Test.hs. Но если я удалю все файлы .hi и .o и загружу в ghci через ghci Test.hs, то память взорвется.


person artella    schedule 19.07.2014    source источник
comment
Я не испытываю разрыва памяти для этой программы ни в ghci, ни при компиляции и запуске. Я использую 7.6.3. Это ошибка компилятора? Также вы можете начать свой ghci с помощью ghci -fobject-code, как указано здесь.   -  person Sibi    schedule 19.07.2014
comment
@Sibi Я могу воспроизвести это в 7.6.3. Если удалить все файлы .hi и .o, а потом загрузить в ghci через ghci Test.hs программа у вас взрывается? Спасибо   -  person artella    schedule 19.07.2014
comment
@Sibi, странно, если я сначала скомпилирую через ghc Test.hs, а затем загружу в ghci, он не взорвется   -  person artella    schedule 19.07.2014
comment
Я могу увидеть некоторый взрыв памяти, если я удалю их и снова протестирую, используя ghci. Я думаю, что лучше проверить эти условия не в ghci.   -  person Sibi    schedule 19.07.2014
comment
Я откатился на предыдущую версию. Мы не помещаем ответы в вопросы на Stack Overflow, потому что, в отличие от многих форумов, вам тоже не нужно пробираться через море меня, и кто-нибудь решил это и тому подобное, чтобы найти ответ, который сработал. Ответ, который лучше всего сработал для спрашивающего, отмечен зеленой галочкой наверху.   -  person AndrewC    schedule 19.07.2014


Ответы (2)


Я полагаю, что это связано с различной обработкой идентификатора longList при :l файле в GHCi, а не при его компиляции.

Когда вы :l ModuleName в GHCi, по умолчанию все идентификаторы верхнего уровня в модуле входят в область действия, так что вы можете эффективно отлаживать его. В вашем примере это включает longList. Это означает, что GHCi сохраняет содержимое longList после его оценки, что приводит к утечке памяти. Я подозреваю, что это так даже с -fobjectcode, поэтому я не уверен, что поведение, обсуждаемое в других комментариях, на самом деле является ошибкой.

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

module Main (main) where

Это означает, что при компиляции GHC может заметить, что все идентификаторы, кроме main, не раскрываются, а longList используется только один раз. Затем он может упасть, сохранив свое значение, избегая утечки памяти.

person Ørjan Johansen    schedule 19.07.2014
comment
Йохансен: Фантастическое спасибо. Добавление module Main (main) where и последующая загрузка в ghci решили все проблемы! :) - person artella; 19.07.2014
comment
@artella Рад, что это помогло, хотя теперь я в замешательстве, потому что я только что сказал, что это должно быть поведением по умолчанию! - person Ørjan Johansen; 19.07.2014
comment
Теперь я обнаружил, что мне нужно использовать как module Main (main) where, так и ghci -fobject-code Test (т.е. смесь вашего ответа и ответа Сиби). Я думаю, что когда я писал комментарий выше, я уже создал псевдоним bashrc, используя -fobject-code, и не осознавал, что это необходимо. - person artella; 19.07.2014
comment
nomeata подтвердила, что это ожидаемое поведение в заявке ghc.haskell.org/trac/ghc/ticket /9332 - person artella; 19.07.2014
comment
@artella Хорошо, это имеет больше смысла, хотя я все еще не думаю, что module Main (main) where должно было что-то изменить. Я прокомментировал трек. - person Ørjan Johansen; 20.07.2014
comment
@ØrjanJohansen Даже если он сохраняет ссылку на longList, я не думаю, что это должно иметь какое-либо значение. Предполагается, что result работает с постоянной памятью, даже если есть ссылка на longList, не так ли? - person Sibi; 20.07.2014
comment
Орьян и Сиби: @Sibi Если я разделю код на два отдельных файла (см. код в ghc.haskell.org/trac/ghc/ticket/9332#comment:10), то кажется, что никакая комбинация возни не может помешать ему взорвать память. - person artella; 20.07.2014
comment
@artella Он взрывается, когда компилируется и запускается? - person Sibi; 20.07.2014
comment
@Sibi: Нет, он взрывается только при запуске через ghci через ghci -fobject-code Main.hs - person artella; 20.07.2014
comment
@artella Что, если добавить еще и -O2? - person Ørjan Johansen; 20.07.2014
comment
@ØrjanJohansen Код в ghc.haskell.org/trac/ghc/ticket /9332#comment:10 подходит для -O2 и даже подходит для -O0. Только когда он работает в ghci, он дует. - person artella; 20.07.2014
comment
@Sibi Обычная оценка result требует, чтобы он также оценивал первые 10000000 элементов longList, после чего преобразователь longList должен был быть обновлен, чтобы содержать эти 10000000 элементов. Только если longList может быть немедленно удален сборщиком мусора, потому что на него больше ничего не ссылается, или, что еще лучше, встроено в result и объединено, result будет иметь шанс избежать утечки памяти в longList. (Технически я думаю, что GHC могла сделать вывод, что longList настолько дешев в расчетах, что его не нужно держать при себе, но я не думаю, что это так.) - person Ørjan Johansen; 20.07.2014
comment
@artella Я имел в виду -O2 как флаг для ghci, я ожидаю, что это что-то сделает, когда у вас также есть -fobject-code. Хотя я думаю, что если -O0 не покажет проблему... - person Ørjan Johansen; 20.07.2014
comment
@ØrjanJohansen Спасибо за объяснение. Теперь это имеет большой смысл. - person Sibi; 20.07.2014
comment
@ØrjanJohansen Ты имеешь в виду ghci -fobject-code -O2 Main.hs ? Я попробовал это, и это все еще взрывается. Спасибо - person artella; 20.07.2014

См. раздел примечание о GHCI:

Если вы заметили утечку памяти при запуске кода в GHCi, обратите внимание, что интерпретируемый код ведет себя иначе, чем скомпилированный код: даже при использовании seq.

Рассмотрим запуск ghci следующим образом:

$ ghci -код объекта

person Sibi    schedule 19.07.2014
comment
Это не работает для меня. Если я удалю все файлы .hi и .o, а затем загружу через ghci -fobject-code Test.hs, память все равно взорвется в Linux. - person artella; 19.07.2014
comment
@artella Да, с -fobject-code я также вижу некоторое увеличение памяти. Вывод здесь заключается в том, что при использовании seq лучше всего протестировать его, скомпилировав в исполняемый файл и затем запустив. - person Sibi; 19.07.2014
comment
это очень странно, потому что при использовании -fobject-code создаются файлы .hi и .o точно так же, как при компиляции через ghc Test.hs. Однако с первым память взрывается, но если я делаю последнее, а затем загружаю в ghci, память не взрывается. Я могу только догадываться, что файлы генерируются по-разному - person artella; 19.07.2014
comment
@artella Но все же этого не должно происходить при использовании -fobject-code. Я не уверен, что вызывает проблему. Возможно, заполните отчет об ошибке, поскольку вы можете воспроизвести это и на 7.8.3? - person Sibi; 19.07.2014
comment
Да, зарегистрирует ошибку. Я могу воспроизвести это в 7.6.3, 7.8.2 и 7.8.3. Спасибо - person artella; 19.07.2014
comment
В конце концов я обнаружил, что мне пришлось использовать комбинацию вашего ответа и ответа Орджана выше, чтобы остановить его взрыв в ghc 7.8.3. Спасибо. - person artella; 19.07.2014
comment
Есть объяснение от nomeata по адресу ghc.haskell.org/trac/ghc/ticket/9332. в отношении вышеуказанного поведения. - person artella; 19.07.2014