Ленивый IO - строка не мусор?
В настоящее время я пытаюсь прочитать содержимое файла XML в Map Int (Map Int String)
и это работает довольно хорошо (используя HaXml). Тем не менее, меня не устраивает потребление памяти моей программой, и проблемы, похоже, связаны с сборкой мусора.
Вот код, который я использую для чтения файла XML:
type TextFile = Map Int (Map Int String)
buildTextFile :: String -> IO TextFile
buildTextFile filename = do content <- readFile filename
let doc = xmlParse filename content
con = docContent (posInNewCxt filename Nothing) doc
return $ buildTF con
Я думаю, что content
сохраняется в памяти даже после возвращения, хотя это не обязательно (конечно, это также может быть doc
или же con
). Я пришел к такому выводу, потому что потребление памяти быстро увеличивается с очень большими файлами XML, хотя в результате TextFile
является только одноэлементной картой одноэлементной карты (с использованием специального файла тестирования, как правило, это другое, конечно). Итак, в конце концов, у меня есть Map
из Map Int String
, только с одной строкой, но потребление памяти составляет до 19 МБ.
Используя строгое приложение ($!
) или используя Data.Text
вместо String
в TextFile
ничего не меняет
Итак, мой вопрос: есть ли способ сказать компилятору, что строка content
(или же doc
или же con
) больше не нужен и что его можно собирать?
И в целом: как я могу узнать, откуда на самом деле возникает проблема, не догадываясь?
Редактировать: как предложил FUZxxl, я попытался использовать deepseq и изменил вторую строку buildTextFile
вот так:
let doc = content `deepseq` xmlParse filename content
К сожалению, это ничего не изменило (или я использую это неправильно?)...
1 ответ
Не угадай, что потребляет память, узнай наверняка
Первый шаг - определить, какие типы потребляют больше всего памяти. Вы можете увидеть множество примеров профилирования кучи здесь в SO или прочитать руководство по GHC.
Принудительное вычисление
Если проблема заключается в отложенной оценке (вы создаете блок в куче, который может вычислить тип документа XML и также оставляете строку в куче), тогда используйте rnf и seq:
buildTextFile :: String -> IO TextFile
buildTextFile filename = do content <- readFile filename
let doc = xmlParse filename content
con = docContent (posInNewCxt filename Nothing) doc
res = buildTF con
return $ rnf res `seq` res
Или просто используйте шаблоны взрыва (!res = buildTF con
), так или иначе, что должно заставить громов и позволить GC собирать String
,