optimize find top n in a list method
diff --git a/src/recon.erl b/src/recon.erl
index efce49f..15ba606 100644
--- a/src/recon.erl
+++ b/src/recon.erl
@@ -260,10 +260,7 @@
AttributeName :: atom(),
Num :: non_neg_integer().
proc_count(AttrName, Num) ->
- lists:sublist(lists:usort(
- fun({_,A,_},{_,B,_}) -> A > B end,
- recon_lib:proc_attrs(AttrName)
- ), Num).
+ sublist_top_n(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.
@@ -297,10 +294,7 @@
proc_window(AttrName, Num, Time) ->
Sample = fun() -> recon_lib:proc_attrs(AttrName) end,
{First,Last} = recon_lib:sample(Time, Sample),
- lists:sublist(lists:usort(
- fun({_,A,_},{_,B,_}) -> A > B end,
- recon_lib:sliding_window(First, Last)
- ), Num).
+ sublist_top_n(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
@@ -317,25 +311,22 @@
%% for more details on refc binaries
-spec bin_leak(pos_integer()) -> [proc_attrs()].
bin_leak(N) ->
- lists:sublist(
- lists:usort(
- fun({K1,V1,_},{K2,V2,_}) -> {V1,K1} =< {V2,K2} end,
- [try
- {ok, {_,Pre,Id}} = recon_lib:proc_attrs(binary, Pid),
- erlang:garbage_collect(Pid),
- {ok, {_,Post,_}} = recon_lib:proc_attrs(binary, Pid),
- {Pid, length(Post)-length(Pre), Id}
- catch
- _:_ -> {Pid, 0, []}
- end || Pid <- processes()]),
- N).
+ 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(Post)-length(Pre), Id}
+ catch
+ _:_ -> {Pid, 0, []}
+ end || Pid <- processes()],
+ N).
%% @doc Shorthand for `node_stats(N, Interval, fun(X,_) -> io:format("~p~n",[X]) end, nostate)'.
-spec node_stats_print(Repeat, Interval) -> term() when
Repeat :: non_neg_integer(),
Interval :: pos_integer().
node_stats_print(N, Interval) ->
- node_stats(N, Interval, fun(X, _) -> io:format("~p~n",[X]) end, ok).
+ node_stats(N, Interval, fun(X, _) -> io:format("~p~n", [X]) end, ok).
%% @doc Because Erlang CPU usage as reported from `top' isn't the most
%% reliable value (due to schedulers doing idle spinning to avoid going
@@ -378,7 +369,7 @@
Stats :: {[Absolutes::{atom(),term()}],
[Increments::{atom(),term()}]}.
node_stats_list(N, Interval) ->
- lists:reverse(node_stats(N, Interval, fun(X,Acc) -> [X|Acc] end, [])).
+ lists:reverse(node_stats(N, Interval, fun(X, Acc) -> [X|Acc] end, [])).
%% @doc Gathers statistics `N' time, waiting `Interval' milliseconds between
%% each run, and accumulates results using a folding function `FoldFun'.
@@ -551,10 +542,7 @@
| 'cnt' | 'oct',
Num :: non_neg_integer().
inet_count(Attr, Num) ->
- lists:sublist(lists:usort(
- fun({_,A,_},{_,B,_}) -> A > B end,
- recon_lib:inet_attrs(Attr)
- ), Num).
+ sublist_top_n(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.
@@ -576,10 +564,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),
- lists:sublist(lists:usort(
- fun({_,A,_},{_,B,_}) -> A > B end,
- recon_lib:sliding_window(First, Last)
- ), Num).
+ sublist_top_n(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
@@ -715,3 +700,27 @@
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) ->
+ sublist_top_n(List, Len, 0, []).
+
+sublist_top_n([], _, _, Acc) ->
+ lists:reverse(Acc);
+sublist_top_n([NewKey = {New1, New2, _}|List], MaxLen, Len, Acc) ->
+ {NewLen, NewAcc} =
+ if Len < MaxLen -> {Len + 1, insert(NewKey, Acc)};
+ true ->
+ [{Small1, Small2, _}|Acc2] = Acc,
+ if {Small2, Small1} < {New2, New1} -> {Len, insert(NewKey, Acc2)};
+ true -> {Len, Acc}
+ end
+ end,
+ sublist_top_n(List, MaxLen, NewLen, NewAcc).
+
+%% @private insert Key in an ordered list
+insert(K, []) -> [K];
+insert({K1, K2, _} = K, [{H1, H2, _} = H|T]) ->
+ if {H2, H1} =< {K2, K1} -> [H|insert(K, T)];
+ true -> [K, H|T]
+ end.
+