Merge pull request #50 from ferd/fix-pheap
Fix pheap
diff --git a/README.md b/README.md
index 77120da..aec9c96 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,8 @@
- 2.3.2 (current Master)
- Allow the `return_to` option in `recon_trace`
+ - More efficient sorting function for procs and ports attributes
+ (thanks to @zhongwencool and @pichi)
- 2.3.1
- Updated `app_deps` script to run with rebar3 dependencies
- Minor docs update
diff --git a/src/recon.erl b/src/recon.erl
index 2a3f83e..1d56897 100644
--- a/src/recon.erl
+++ b/src/recon.erl
@@ -260,7 +260,7 @@
AttributeName :: atom(),
Num :: non_neg_integer().
proc_count(AttrName, Num) ->
- sublist_top_n(recon_lib:proc_attrs(AttrName), Num).
+ recon_lib:sublist_top_n_attrs(recon_lib:proc_attrs(AttrName), Num).
%% @doc Fetches a given attribute from all processes (except the
%% caller) and returns the biggest entries, over a sliding time window.
@@ -294,7 +294,7 @@
proc_window(AttrName, Num, Time) ->
Sample = fun() -> recon_lib:proc_attrs(AttrName) end,
{First,Last} = recon_lib:sample(Time, Sample),
- sublist_top_n(recon_lib:sliding_window(First, Last), Num).
+ recon_lib:sublist_top_n_attrs(recon_lib:sliding_window(First, Last), Num).
%% @doc Refc binaries can be leaking when barely-busy processes route them
%% around and do little else, or when extremely busy processes reach a stable
@@ -311,15 +311,16 @@
%% for more details on refc binaries
-spec bin_leak(pos_integer()) -> [proc_attrs()].
bin_leak(N) ->
- Procs = sublist_top_n([try
- {ok, {_,Pre,Id}} = recon_lib:proc_attrs(binary, Pid),
- erlang:garbage_collect(Pid),
- {ok, {_,Post,_}} = recon_lib:proc_attrs(binary, Pid),
- {Pid, length(Pre) - length(Post), Id}
- catch
- _:_ -> {Pid, {0, 0}, []}
- end || Pid <- processes()],
- N),
+ Procs = recon_lib:sublist_top_n_attrs([
+ try
+ {ok, {_,Pre,Id}} = recon_lib:proc_attrs(binary, Pid),
+ erlang:garbage_collect(Pid),
+ {ok, {_,Post,_}} = recon_lib:proc_attrs(binary, Pid),
+ {Pid, length(Pre) - length(Post), Id}
+ catch
+ _:_ -> {Pid, {0, 0}, []}
+ end || Pid <- processes()
+ ], N),
[{Pid, -Val, Id} ||{Pid, Val, Id} <-Procs].
%% @doc Shorthand for `node_stats(N, Interval, fun(X,_) -> io:format("~p~n",[X]) end, nostate)'.
@@ -543,7 +544,7 @@
| 'cnt' | 'oct',
Num :: non_neg_integer().
inet_count(Attr, Num) ->
- sublist_top_n(recon_lib:inet_attrs(Attr), Num).
+ recon_lib:sublist_top_n_attrs(recon_lib:inet_attrs(Attr), Num).
%% @doc Fetches a given attribute from all inet ports (TCP, UDP, SCTP)
%% and returns the biggest entries, over a sliding time window.
@@ -565,7 +566,7 @@
inet_window(Attr, Num, Time) when is_atom(Attr) ->
Sample = fun() -> recon_lib:inet_attrs(Attr) end,
{First,Last} = recon_lib:sample(Time, Sample),
- sublist_top_n(recon_lib:sliding_window(First, Last), Num).
+ recon_lib:sublist_top_n_attrs(recon_lib:sliding_window(First, Last), Num).
%% @doc Allows to be similar to `erlang:port_info/1', but allows
%% more flexible port usage: usual ports, ports that were registered
@@ -701,43 +702,3 @@
named_rpc(Node, Fun, Timeout) when is_atom(Node) ->
named_rpc([Node], Fun, Timeout).
-%% @private Returns the top n element of List. n = Len
-sublist_top_n(List, Len) ->
- pheap_fill(List, Len, []).
-
-pheap_fill(List, 0, Heap) ->
- pheap_full(List, Heap);
-pheap_fill([], 0, Heap) ->
- pheap_to_list(Heap, []);
-pheap_fill([{Y, X, _} = H|T], N, Heap) ->
- pheap_fill(T, N-1, insert({{X, Y}, H}, Heap)).
-
-pheap_full([], Heap) ->
- pheap_to_list(Heap, []);
-pheap_full([{Y, X, _} = H|T], [{K, _}|HeapT] = Heap) ->
- case {X, Y} of
- N when N > K ->
- pheap_full(T, insert({N, H}, merge_pairs(HeapT)));
- _ ->
- pheap_full(T, Heap)
- end.
-
-pheap_to_list([], Acc) -> Acc;
-pheap_to_list([{_, H}|T], Acc) ->
- pheap_to_list(merge_pairs(T), [H|Acc]).
-
--compile({inline, [ insert/2
- , merge/2
- ]}).
-
-insert(E, []) -> [E]; %% merge([E], H)
-insert(E, [E2|_] = H) when E =< E2 -> [E, H];
-insert(E, [E2|H]) -> [E2, [E]|H].
-
-merge(H1, []) -> H1;
-merge([E1|H1], [E2|_]=H2) when E1 =< E2 -> [E1, H2|H1];
-merge(H1, [E2|H2]) -> [E2, H1|H2].
-
-merge_pairs([]) -> [];
-merge_pairs([H]) -> H;
-merge_pairs([A, B|T]) -> merge(merge(A, B), merge_pairs(T)).
diff --git a/src/recon_lib.erl b/src/recon_lib.erl
index 5a49cf3..8bae1db 100644
--- a/src/recon_lib.erl
+++ b/src/recon_lib.erl
@@ -12,7 +12,8 @@
triple_to_pid/3, term_to_pid/1,
term_to_port/1,
time_map/5, time_fold/6,
- scheduler_usage_diff/2]).
+ scheduler_usage_diff/2,
+ sublist_top_n_attrs/2]).
%% private exports
-export([binary_memory/1]).
@@ -224,7 +225,53 @@
lists:zip(lists:sort(First), lists:sort(Last))
).
+%% @doc Returns the top n element of a list of process or inet attributes
+-spec sublist_top_n_attrs([Attrs], pos_integer()) -> [Attrs]
+ when Attrs :: recon:proc_attrs() | recon:inet_attrs().
+sublist_top_n_attrs(_, 0) ->
+ %% matching lists:sublist/2 behaviour
+ [];
+sublist_top_n_attrs(List, Len) ->
+ pheap_fill(List, Len, []).
+
%% @private crush binaries from process_info into their amount of place
%% taken in memory.
binary_memory(Bins) ->
lists:foldl(fun({_,Mem,_}, Tot) -> Mem+Tot end, 0, Bins).
+
+%%%%%%%%%%%%%%%
+%%% PRIVATE %%%
+%%%%%%%%%%%%%%%
+pheap_fill(List, 0, Heap) ->
+ pheap_full(List, Heap);
+pheap_fill([], _, Heap) ->
+ pheap_to_list(Heap, []);
+pheap_fill([{Y, X, _} = H|T], N, Heap) ->
+ pheap_fill(T, N-1, insert({{X, Y}, H}, Heap)).
+
+pheap_full([], Heap) ->
+ pheap_to_list(Heap, []);
+pheap_full([{Y, X, _} = H|T], [{K, _}|HeapT] = Heap) ->
+ case {X, Y} of
+ N when N > K ->
+ pheap_full(T, insert({N, H}, merge_pairs(HeapT)));
+ _ ->
+ pheap_full(T, Heap)
+ end.
+
+pheap_to_list([], Acc) -> Acc;
+pheap_to_list([{_, H}|T], Acc) ->
+ pheap_to_list(merge_pairs(T), [H|Acc]).
+
+-compile({inline, [insert/2, merge/2]}).
+insert(E, []) -> [E]; %% merge([E], H)
+insert(E, [E2|_] = H) when E =< E2 -> [E, H];
+insert(E, [E2|H]) -> [E2, [E]|H].
+
+merge(H1, []) -> H1;
+merge([E1|H1], [E2|_]=H2) when E1 =< E2 -> [E1, H2|H1];
+merge(H1, [E2|H2]) -> [E2, H1|H2].
+
+merge_pairs([]) -> [];
+merge_pairs([H]) -> H;
+merge_pairs([A, B|T]) -> merge(merge(A, B), merge_pairs(T)).
diff --git a/test/recon_lib_SUITE.erl b/test/recon_lib_SUITE.erl
index 4a69df3..5c022c8 100644
--- a/test/recon_lib_SUITE.erl
+++ b/test/recon_lib_SUITE.erl
@@ -2,7 +2,7 @@
-include_lib("common_test/include/ct.hrl").
-compile(export_all).
-all() -> [scheduler_usage_diff].
+all() -> [scheduler_usage_diff, sublist_top_n].
scheduler_usage_diff(_Config) ->
{Active0, Total0} = {1000, 2000},
@@ -15,3 +15,15 @@
% Check for 100% usage
SchedStat2 = {1, Active0 + 1000, Total0 + 1000},
[{1, 1.0}] = recon_lib:scheduler_usage_diff([SchedStat0], [SchedStat2]).
+
+sublist_top_n(_Config) ->
+ L0 = [1,1,2,4,5,6,0,8,7,4,5,2,1,8,agbg,{t},3,[bah],"te",<<"bin">>,23.0, 23],
+ L = [{make_ref(), Val, [{meta,data}]} || Val <- L0],
+ %% former sort function used prior to integraton of sublist_top_n
+ Sorted = lists:usort(fun({_,A,_},{_,B,_}) -> A > B end, L),
+ [begin
+ Sub = (catch lists:sublist(Sorted, N)),
+ ct:pal("Sub ~p: ~p", [N, Sub]),
+ Sub = (catch recon_lib:sublist_top_n_attrs(L, N))
+ end || N <- lists:seq(0, length(L)+1)],
+ ok.