Sla*_*bko 5 profiling haskell ghc
我试图从GHC分析器中理解.有一个相当简单的应用程序,它使用werq和lens-aeson库,在学习GHC分析时,我决定玩它一点.
使用不同的选项(time工具+RTS -p -RTS和+RTS -p -h)我获得了完全不同的内存使用量.拥有所有这些数字,我现在完全迷失了,试图了解正在发生的事情,以及应用程序实际使用了多少内存.
这种情况让我想起了亚瑟布洛赫的一句话:"一个有手表的男人知道现在是什么时候.一个有两只手表的男人永远不会确定."
请你,建议我,我如何阅读所有这些数字,以及每个数字的含义是什么.
这是数字:
time -l报道大约19M
#/usr/bin/time -l ./simple-wreq
...
3.02 real 0.39 user 0.17 sys
19070976 maximum resident set size
0 average shared memory size
0 average unshared data size
0 average unshared stack size
21040 page reclaims
0 page faults
0 swaps
0 block input operations
0 block output operations
71 messages sent
71 messages received
2991 signals received
43 voluntary context switches
6490 involuntary context switches
Run Code Online (Sandbox Code Playgroud)
使用+RTS -p -RTS大约92M的标志报告.虽然它说"总分配"对我来说似乎很奇怪,像这样一个简单的应用程序可以分配和释放91M
# ./simple-wreq +RTS -p -RTS
# cat simple-wreq.prof
Fri Oct 14 15:08 2016 Time and Allocation Profiling Report (Final)
simple-wreq +RTS -N -p -RTS
total time = 0.07 secs (69 ticks @ 1000 us, 1 processor)
total alloc = 91,905,888 bytes (excludes profiling overheads)
COST CENTRE MODULE %time %alloc
main.g Main 60.9 88.8
MAIN MAIN 24.6 2.5
decodeLenient/look Data.ByteString.Base64.Internal 5.8 2.6
decodeLenientWithTable/fill Data.ByteString.Base64.Internal 2.9 0.1
decodeLenientWithTable.\.\.fill Data.ByteString.Base64.Internal 1.4 0.0
decodeLenientWithTable.\.\.fill.\ Data.ByteString.Base64.Internal 1.4 0.1
decodeLenientWithTable.\.\.fill.\.\.\.\ Data.ByteString.Base64.Internal 1.4 3.3
decodeLenient Data.ByteString.Base64.Lazy 1.4 1.4
individual inherited
COST CENTRE MODULE no. entries %time %alloc %time %alloc
MAIN MAIN 443 0 24.6 2.5 100.0 100.0
main Main 887 0 0.0 0.0 75.4 97.4
main.g Main 889 0 60.9 88.8 75.4 97.4
object_ Data.Aeson.Parser.Internal 925 0 0.0 0.0 0.0 0.2
jstring_ Data.Aeson.Parser.Internal 927 50 0.0 0.2 0.0 0.2
unstream/resize Data.Text.Internal.Fusion 923 600 0.0 0.3 0.0 0.3
decodeLenient Data.ByteString.Base64.Lazy 891 0 1.4 1.4 14.5 8.1
decodeLenient Data.ByteString.Base64 897 500 0.0 0.0 13.0 6.7
....
Run Code Online (Sandbox Code Playgroud)
+RTS -p -h并向hp2ps我展示以下图片和两个数字:标题中的114K和图表上的1.8Mb左右.

而且,以防万一,这是应用程序:
module Main where
import Network.Wreq
import Control.Lens
import Data.Aeson.Lens
import Control.Monad
main :: IO ()
main = replicateM_ 10 g
where
g = do
r <- get "http://httpbin.org/get"
print $ r ^. responseBody
. key "headers"
. key "User-Agent"
. _String
Run Code Online (Sandbox Code Playgroud)
更新1:感谢大家的不错回应.正如所建议的那样,我添加了+RTS -s输出,因此整个画面都为每个阅读它的人构建.
#./simple-wreq +RTS -s
...
128,875,432 bytes allocated in the heap
32,414,616 bytes copied during GC
2,394,888 bytes maximum residency (16 sample(s))
355,192 bytes maximum slop
7 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 194 colls, 0 par 0.018s 0.022s 0.0001s 0.0022s
Gen 1 16 colls, 0 par 0.027s 0.031s 0.0019s 0.0042s
Run Code Online (Sandbox Code Playgroud)
更新2:可执行文件的大小:
#du -h simple-wreq
63M simple-wreq
Run Code Online (Sandbox Code Playgroud)
有手表的男人知道现在几点了.一个有两只手表的男人永远不会确定.
啊,但两只手表显示什么?两者都打算以UTC显示当前时间吗?或者其中一个应该用UTC显示时间,另一个应该在火星上的某个点上显示时间?只要它们同步,第二种情况就不会成为问题,对吧?
而这正是这里发生的事情.您比较不同的内存测量:
最大驻留时间是程序在给定时间使用的最大内存量.那是19MB.但是,分配内存的总量要多得多,因为这就是GHC的工作方式:它为垃圾收集的对象"分配"内存,这几乎是所有未解包的内容.
让我们为此检查一个C示例:
int main() {
int i;
char * mem;
for(i = 0; i < 5; ++i) {
mem = malloc(19 * 1000 * 1000);
free(mem);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
每当我们使用时malloc,我们将分配19兆字节的内存.但是,我们之后立即释放内存.因此,我们在一个点上拥有的最大内存量是19兆字节(并且对于堆栈和程序本身来说更多一点).
但总的来说,我们总共分配5*19M,95M.不过,我们可以运行我们的小程序,只需20兆美元的RAM.这是总分配内存和最大驻留时间之间的差异.请注意,按时间报告的驻留时间总是至少du <executable>,因为它也必须驻留在内存中.
话虽如此,生成统计数据的最简单方法是-s,它将显示Haskell程序的最大居住点是什么.在您的情况下,它将是1.9M,您的堆配置文件中的数字(或由于分析的两倍).是的,Haskell可执行文件往往变得非常大,因为库是静态链接的.