Раздуване на паметта за стриктно събиране/стриктно сгъване в 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
Orjan & Sibi : @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, след което thunk на 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 -fobject-код

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
В крайна сметка открих, че трябва да използвам комбинация от вашия отговор и отговора на Orjan по-горе, за да спра да се взривява в ghc 7.8.3. Благодаря. - person artella; 19.07.2014
comment
Има обяснение от nomeata на ghc.haskell.org/trac/ghc/ticket/9332 по отношение на горното поведение. - person artella; 19.07.2014