глубокая сборка мусора, поверхностная – это дело обычное. Теперь посмотрим на показатели строгой версии
этой программы:
module Main where
import Data.List(foldl’)
sum’ = foldl’ (+) 0
main = print $ sum’ [1 .. 1e5]
Скомпилируем:
$ ghc --make sumStrict.hs -rtsopts -fforce-recomp
Посмотрим на статистику:
$ ./sumStrict +RTS -sstderr
5.00005e9
10,474,128 bytes allocated in the heap
24,324 bytes copied during GC
27,072 bytes maximum residency (1 sample(s))
27,388 bytes maximum slop
1 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed)
Avg pause
Max pause
Gen
0
19 colls,
0 par
0.00s
0.00s
0.0000s
0.0000s
Gen
1
1 colls,
0 par
0.00s
0.00s
0.0001s
0.0001s
INIT
time
0.00s
(
0.00s elapsed)
MUT
time
0.01s
(
0.01s elapsed)
GC
time
0.00s
(
0.00s elapsed)
EXIT
time
0.00s
(
0.00s elapsed)
Total
time
0.01s
(
0.01s elapsed)
%GC
time
0.0%
(3.0% elapsed)
Alloc rate
1,309,266,000 bytes per MUT second
Productivity 100.0% of total user, 116.0% of total elapsed
Мы видим, что произошла лишь одна глубокая сборка. И это существенно сказалось на продуктивности.
Кромке того мы видим, что программа заняла лишь 27 Кб памяти, вместо 2 Мб как в прошлом случае. Теперь
давайте покрутим ручки у GC. В GHC можно устанавливать разные параметры сборки мусора с помощью
флагов. Все флаги можно посмотреть в документации GHC. Мы обратим внимание на несколько флагов.
Флаг H назначает минимальное значение для стартового объёма кучи. Флаг A назначает объём памяти для
яслей. По умолчанию размер яслей равен 512 Кб (эта цифра может измениться). Изменением этих параметров
мы можем отдалить сборку мусора. Чем дольше работает программа между сборками, тем выше вероятность
того, что многие объекты уже не нужны.
Давайте убедимся в том, что поверхностные очистки происходят очень быстро и совсем не тормозят
программу. Установим размер яслей на 32 Кб вместо 512 Кб как по умолчанию (размер пишется сразу за
флагом, за цифрой идёт k или m):
$ ./sumStrict +RTS -A32k -sstderr
...
Tot time (elapsed)
Avg pause
Max pause
Gen
0
318 colls,
0 par
0.00s
0.00s
0.0000s
0.0000s
Gen
1
1 colls,
0 par
0.00s
0.00s
0.0001s
0.0001s
...
MUT
time
0.01s
(
0.01s elapsed)
GC
time
0.00s
(
0.00s elapsed)
...
%GC
time
0.0%
(11.8% elapsed)
Статистика выполнения программы | 165
Мы видим, что за счёт уменьшения памяти очистки существенно участились, но это не сказалось на об-
щем результате. С помощью флага H[size] мы можем устанавливать рекомендуемое минимальное значение
для размера кучи. Оно точно не будет меньше. Вернёмся к первому варианту и выделим алгоритму побольше
памяти, например 20 Мб:
./sum +RTS -A1m -H20m -sstderr
5.00005e9
14,145,284 bytes allocated in the heap
319,716 bytes copied during GC
324,136 bytes maximum residency (1 sample(s))
60,888 bytes maximum slop
22 MB total memory in use (1 MB lost due to fragmentation)
Tot time (elapsed)
Avg pause
Max pause
Gen
0
2 colls,
0 par
0.00s
0.00s
0.0001s
0.0001s
Gen
1
1 colls,
0 par
0.00s
0.00s
0.0007s
0.0007s
INIT
time
0.00s
(
0.00s elapsed)
MUT
time
0.02s
(
0.02s elapsed)
GC
time
0.00s
(
0.00s elapsed)
EXIT
time
0.00s
(
0.00s elapsed)
Total
time
0.02s
(
0.02s elapsed)
%GC
time
0.0%
(4.4% elapsed)
Alloc rate
884,024,998 bytes per MUT second
Productivity 100.0% of total user, 78.6% of total elapsed
Произошла лишь одна глубокая очистка (похоже, что эта очистка соответствует начальному выделению
памяти) и продуктивность программы стала стопроцентной. С помощью флага S вместо s мы можем по-
смотреть более детальную картину управления памяти. Будут распечатаны показатели памяти для каждой
очистки.
./sum +RTS -Sfile
В файле file мы найдём такую таблицу:
память
время
выделено скопировано в живых
GC
Total
Тип очистки
Alloc
Copied
Live
GC
GC
TOT
TOT
Page Flts
bytes
bytes
bytes
user
elap
user
elap
545028
150088
174632
0.00
0.00
0.00
0.00
0
0
(Gen:
1)
523264
298956
324136
0.00
0.00
0.00
0.00
0
0
(Gen:
0)
...
Итак у нас появился один существенный показатель качества программ. Это количество глубоких очи-
сток. Во время глубокой очистки вычислитель производит две затратные операции: сканирование всей кучи