2. cprof - The Call Count Profiler

2 cprof - 调用计数分析器

cprof 是一个分析工具,可用于了解系统中不同功能被调用的频率。

cprof使用类似于本地呼叫追踪的断点,但包含计数器,以收集分析数据。因此,不需要特殊编译要分析的任何模块。

cprof以减少的总呼叫计数顺序呈现所有分析模块,并且对于每个模块,还以降低的呼叫计数顺序呈现所有分析功能。可以指定呼叫计数限制以过滤低于限制的所有功能。

分析按以下步骤进行:

cprof:start/0..3通过在其上设置呼叫计数断点,开始使用指定功能的归零呼叫计数器进行分析。Mod:Fun()运行要分析的代码。cprof:pause/0..3暂停指定功能的呼叫计数器。这最大限度地减少了运行在后台或外壳中的干扰分析的代码的影响。呼叫计数器在主机字长度“达到上限”时自动暂停。对于32位主机,最大计数器值为2147483647. cprof:analyse/0..2收集呼叫计数器并计算结果。cprof:restart/0..3对于指定的功能,从零开始重新启动计数器。可以用来收集一组新的计数器,而不必停止并开始呼叫计数分析。cprof:stop/0..3通过从指定函数中删除调用计数断点来停止分析。

可以将函数指定为系统中的全部,全部位于一个模块中,所有模块中的一个函数,一个函数或所有尚未加载的模块中的所有函数。到目前为止,BIF不能被追踪电话号码。

分析结果可以是所有模块,也可以是一个模块。在任何一种情况下,都可以给出一个呼叫计数限制,以在呼叫计数低于限制的情况下过滤功能。所有模块的分析并没有包含该模块cprof本身,它只能通过指定它作为一个单一模块,用于分析进行分析。

与其他形式的跟踪相比,呼叫计数跟踪非常​​轻便,因为不需要生成跟踪消息。一些测量表明在10%附近的性能下降。

以下部分显示了使用分析的一些示例cprof。另见cprof(3)

2.1例:背景工作

从Erlang shell:

1> cprof:start(), cprof:pause(). % Stop counters just after start 3476 2> cprof:analyse(). {30, [{erl_eval,11, [{{erl_eval,expr,3},3}, {{erl_eval,'-merge_bindings/2-fun-0-',2},2}, {{erl_eval,expand_module_name,2},1}, {{erl_eval,merge_bindings,2},1}, {{erl_eval,binding,2},1}, {{erl_eval,expr_list,5},1}, {{erl_eval,expr_list,3},1}, {{erl_eval,exprs,4},1}]}, {orddict,8, [{{orddict,find,2},6}, {{orddict,dict_to_list,1},1}, {{orddict,to_list,1},1}]}, {packages,7,[{{packages,is_segmented_1,1},6}, {{packages,is_segmented,1},1}]}, {lists,4,[{{lists,foldl,3},3},{{lists,reverse,1},1}]}]} 3> cprof:analyse(cprof). {cprof,3,[{{cprof,tr,2},2},{{cprof,pause,0},1}]} 4> cprof:stop(). 3476

这个例子展示了shell为了解释第一个命令行而执行的后台工作。大部分工作都是由erl_eval和完成的orddict

本例中捕获的内容是shell在解释实际调用to cprof:start()和之间发生的命令行时所执行的部分工作cprof:analyse()

2.2示例:一个模块

从Erlang shell:

1> cprof:start(),R=calendar:day_of_the_week(1896,4,27),cprof:pause(),R. 1 2> cprof:analyse(calendar). {calendar,9, [{{calendar,df,2},1}, {{calendar,dm,1},1}, {{calendar,dy,1},1}, {{calendar,last_day_of_the_month1,2},1}, {{calendar,last_day_of_the_month,2},1}, {{calendar,is_leap_year1,1},1}, {{calendar,is_leap_year,1},1}, {{calendar,day_of_the_week,3},1}, {{calendar,date_to_gregorian_days,3},1}]} 3> cprof:stop(). 3271

这个例子告诉我们“Aktiebolaget LM Ericsson&Co”是星期一注册的(因为第一个命令的返回值是1),并且calendar模块需要9个函数调用来计算它。

使用cprof:analyse()在此示例中,还显示了与第一个示例中大致相同的背景工作。

2.3示例:在代码中

编写一个模块:

-module(sort). -export([do/1]). do(N) -> cprof:stop(), cprof:start(), do(N, []). do(0, L) -> R = lists:sort(L), cprof:pause(), R; do(N, L) -> do(N-1, [random:uniform(256)-1 | L]).

从Erlang shell:

1> c(sort). {ok,sort} 2> l(random). {module,random} 3> sort:do(1000). [0,0,1,1,1,1,1,1,2,2,2,3,3,3,3,3,4,4,4,5,5,5,5,6,6,6,6,6,6|...] 4> cprof:analyse(). {9050, [{lists_sort,6047, [{{lists_sort,merge3_2,6},923}, {{lists_sort,merge3_1,6},879}, {{lists_sort,split_2,5},661}, {{lists_sort,rmerge3_1,6},580}, {{lists_sort,rmerge3_2,6},543}, {{lists_sort,merge3_12_3,6},531}, {{lists_sort,merge3_21_3,6},383}, {{lists_sort,split_2_1,6},338}, {{lists_sort,rmerge3_21_3,6},299}, {{lists_sort,rmerge3_12_3,6},205}, {{lists_sort,rmerge2_2,4},180}, {{lists_sort,rmerge2_1,4},171}, {{lists_sort,merge2_1,4},127}, {{lists_sort,merge2_2,4},121}, {{lists_sort,mergel,2},79}, {{lists_sort,rmergel,2},27}]}, {random,2001, [{{random,uniform,1},1000}, {{random,uniform,0},1000}, {{random,seed0,0},1}]}, {sort,1001,[{{sort,do,2},1001}]}, {lists,1,[{{lists,sort,1},1}]}]} 5> cprof:stop(). 5369

这个例子显示了一些lists:sort/1工作原理的细节。它在模块中使用了6047个函数调用lists_sort来完成工作。

这一次,由于shell没有涉及,因此在分析过程中没有其他工作在系统中完成。如果您尝试使用新启动的Erlang模拟器重试相同的示例,但省略了该命令l(random),则分析将显示更多由code_server其他人完成的自动加载模块的函数调用random