необходимо указать флаг caf-all. Попробуем на таком модуле:
module Main where
fun1 = test concatL - test concatR
fun2 = test concatL + test concatR
Статистика выполнения программы | 167
test f = last $ f $ map return [1 .. 1e4]
concatR = foldr (++) []
concatL = foldl (++) []
main = print fun1 >> print fun2
Скомпилируем:
$ ghc --make concat2.hs -rtsopts -prof -auto-all -caf-all -fforce-recomp
$ ./concat2 +RTS -p
0.0
20000.0
После этого можно открыть файл concat2. prof и посмотреть итоговую статистику по всем значениям.
Программа с включённым профилированием будет работать гораздо медленей, не исключено, что ей не
хватит памяти на стеке, в этом случае вы можете добавить памяти с помощью флага вычислителя K, впрочем
если это произойдёт GHC подскажет вам что делать.
Динамика изменения объёма кучи
В предыдущем разделе мы смотрели общее время и память затраченные на вычисление функции. В этом
мы научимся измерять динамику изменения расхода памяти на куче. По этому показателю можно понять
в какой момент в программе возникают утечки памяти. Мы увидим характерные горбы на картинках, ко-
гда GC будет активно запрашивать новую память. Для этого сначала нужно скомпилировать программу с
флагом prof как и в предыдущем разделе, а при выполнении программы добавить один из флагов hc, hm,
hd, hy или hr. Все они начинаются с буквы h, от слова heap (куча). Вторая буква указывает тип графика,
какими показателями мы интересуемся. Все они создают специальный файл имяПриложения. hp, который мы
можем преобразовать в график в формате PostScript с помощью программы hp2ps, она устанавливается
автоматически вместе с GHC.
Рассмотрим типичную утечку памяти (из упражнения к предыдущей главе):
module Main where
import System.Environment(getArgs)
main = print . sum2 . xs . read =<< fmap head getArgs
where xs n = [1 .. 10 ^ n]
sum2 :: [Int] -> (Int, Int)
sum2 = iter (0, 0)
where iter c
[]
= c
iter c
(x:xs) = iter (tick x c) xs
tick :: Int -> (Int, Int) -> (Int, Int)
tick x (c0, c1) | even x
= (c0, c1 + 1)
| otherwise = (c0 + 1, c1)
Скомпилируем с флагом профилирования:
$ ghc --make leak.hs -rtsopts -prof -auto-all
Статистика вычислителя показывает, что эта программа вызывала глубокую очистку 8 раз и выполняла
полезную работу лишь 40% времени.
$ ./leak 6 +RTS -K30m -sstderr
...
Tot time (elapsed)
Avg pause
Max pause
Gen
0
493 colls,
0 par
0.26s
0.26s
0.0005s
0.0389s
Gen
1
8 colls,
0 par
0.14s
0.20s
0.0248s
0.0836s
...
Productivity
40.5% of total user, 35.6% of total elapsed
Теперь посмотрим на профиль кучи.
168 | Глава 10: Реализация Haskell в GHC
$ ./leak 6 +RTS -K30m -hc
(500000,500000)
$ hp2ps -e80mm -c leak.hp
В первой команде мы добавили флаг hc для того, чтобы создать файл с расширением . hp. Он содержит
таблицу с показателями размера кучи, которые вычислитель замеряет через равные промежутки времени. Мы
можем изменять интервал с помощью флага iN, где N – время в секундах. Второй командой мы преобразуем
профиль в картинку. Флаг c, говорит о том, что мы хотим получить цветную картинку, а флаг e80mm, говорит
о том, что мы собираемся вставить картинку в текст LaTeX. После e указан размер в миллиметрах. Мы видим
характерный горб (рис. 10.10).
leak 6 +RTS -K30m -hc
3,008,476 bytes x seconds
Fri Jun 1 21:17 2012
bytes
14M
12M
(103)tick/sum2.iter/sum2/m...
10M
8M
(102)main.xs/main/Main.CAF
6M
4M
(101)sum2.iter/sum2/main/M...
2M
0M
0.0
0.1
0.1
0.2
0.2
0.2
seconds
Рис. 10.10: Профиль кучи для утечки памяти
В картинку не поместились имена функций мы можем увеличить строку флагом L. Теперь все имена
поместились (рис. 10.11).
$ ./leak 6 +RTS -K30m -hc -L45
(500000,500000)
$ hp2ps -e80mm -c leak.hp
С помощью флага hd посмотрим на объекты, которые застряли в куче (рис. 10.12):
$ ./leak 6 +RTS -K30m -hd -L45
(500000,500000)
$ hp2ps -e80mm -c leak.hp
Теперь куча разбита по типу объектов (замыканий) (рис. 10.12). BLACKHOLE это специальный объект, ко-
торый заменяет THUNK во время его вычисления. I# – это скрытый конструктор Int. sat_sUa и sat_sUd – это
имена застрявших отложенных вычислений. Если бы наша программа была очень большой на этом месте мы
бы запустили профилирование по функциям с флагом p и из файла leak. prof узнали бы в каких функциях