Вот что я вижу (после вставки print
перед последним performGC
, чтобы помочь пометить, когда что-то произойдет.
524288 524296 32381000 0.00 0.00 1.15 1.95 0 0 (Gen: 0)
524288 524296 31856824 0.00 0.00 1.16 1.96 0 0 (Gen: 0)
368248 808 1032992 0.00 0.02 1.16 1.99 0 0 (Gen: 1)
0 808 1032992 0.00 0.00 1.16 1.99 0 0 (Gen: 1)
"performed!"
39464 2200 1058952 0.00 0.00 1.16 1.99 0 0 (Gen: 1)
22264 1560 1075992 0.00 0.00 1.16 2.00 0 0 (Gen: 0)
0 0.00 0.00
Так что после ГХ в куче все еще 1M (без -G1). С -G1 я вижу:
34340656 20520040 20524800 0.10 0.12 0.76 0.85 0 0 (Gen: 0)
41697072 24917800 24922560 0.12 0.14 0.91 1.01 0 0 (Gen: 0)
70790776 800 2081568 0.00 0.02 1.04 1.20 0 0 (Gen: 0)
0 800 2081568 0.00 0.00 1.04 1.20 0 0 (Gen: 0)
"performed!"
39464 2184 1058952 0.00 0.00 1.05 1.21 0 0 (Gen: 0)
22264 2856 43784 0.00 0.00 1.05 1.21 0 0 (Gen: 0)
0 0.00 0.00
То есть около 2 млн. Это на x86_64 / Linux.
Давайте подумаем о модели хранения машины STG допосмотрите, есть ли что-то еще в куче.
Вещи, которые могут быть в этом 1М пространства:
- CAF для таких вещей, как
[]
, строковые константы и маленькие Int
и Char
пул, плюс вещи в библиотеках, stdin
MVar? - Объекты состояния потока (TSO) для потока
main
. - Anyвыделенные обработчики сигналов.
- Код Хаскелла менеджера ввода-вывода.
- Искры в пуле искр
Из опыта эта цифра чуть меньше 1М кажетсяпо умолчанию «след» двоичного файла GHC. Это примерно то же, что я видел и в других программах (например, программа для стрельбыверсия менее 900K).
Возможно, профилировщик может что-то сказать.Вот профиль -hT
(библиотеки профилирования не требуются) после того, как я вставляю минимальный цикл занятости в конце, чтобы выстроить хвост:
$ ./A +RTS -K10M -S -hT -i0.001
Результаты на этом графике:
Победа!Посмотрите на этот объект стека потоков ~ 1M, который находится там!
Я не знаю, как сделать TSO меньше.
Код, который создал приведенный выше график:
import Language.Haskell.Exts.Annotated -- from haskell-src-exts
import System.Mem
import System.IO
import Data.Int
import Control.Exception
main :: IO ()
main = do
evaluate $ length $ show $ fromParseResult
$ parseFileContents
$ "data C = C {a :: F {- " ++ replicate 400000 'd' ++ " -} }"
performGC
performGC
print "performed!"
performGC
-- busy loop so we can sample what's left on the heap.
let go :: Int32 -> IO ()
go 0 = return ()
go n = go $! n-1
go (maxBound :: Int32)