From bfae535d4ac51d2c3bef146e0f058e105bb5e956 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Tue, 22 Oct 2013 13:37:03 +0200 Subject: observer: Optimize row lookups Use arrays instead of lists to cache data, gives faster lookups for large contents. Also update colors used in table viewer, indication new and changed rows. Other minor bugfixes in tables viewer. --- lib/observer/src/observer_pro_wx.erl | 73 ++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 29 deletions(-) (limited to 'lib/observer/src/observer_pro_wx.erl') diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl index 10e2f12e0f..0be8c18893 100644 --- a/lib/observer/src/observer_pro_wx.erl +++ b/lib/observer/src/observer_pro_wx.erl @@ -66,6 +66,7 @@ -record(holder, {parent, info, + etop, sort=#sort{}, accum=[], attrs, @@ -435,13 +436,14 @@ set_focus([Old|_], [New|_], Grid) -> init_table_holder(Parent, Attrs) -> Backend = spawn_link(node(), observer_backend,etop_collect,[self()]), table_holder(#holder{parent=Parent, - info=#etop_info{procinfo=[]}, + etop=#etop_info{}, + info=array:new(), node=node(), backend_pid=Backend, attrs=Attrs }). -table_holder(#holder{info=#etop_info{procinfo=Info}, attrs=Attrs, +table_holder(#holder{info=Info, attrs=Attrs, node=Node, backend_pid=Backend}=S0) -> receive {get_row, From, Row, Col} -> @@ -488,7 +490,8 @@ table_holder(#holder{info=#etop_info{procinfo=Info}, attrs=Attrs, From ! {self(), S0#holder.accum == true}, table_holder(S0); {dump, Fd} -> - etop_txt:do_update(Fd, S0#holder.info, #opts{node=Node}), + EtopInfo = (S0#holder.etop)#etop_info{procinfo=array:to_list(Info)}, + etop_txt:do_update(Fd, EtopInfo, #opts{node=Node}), file:close(Fd), table_holder(S0); stop -> @@ -498,23 +501,23 @@ table_holder(#holder{info=#etop_info{procinfo=Info}, attrs=Attrs, table_holder(S0) end. -change_sort(Col, S0=#holder{parent=Parent, info=EI=#etop_info{procinfo=Data}, sort=Sort0}) -> +change_sort(Col, S0=#holder{parent=Parent, info=Data, sort=Sort0}) -> {Sort, ProcInfo}=sort(Col, Sort0, Data), - Parent ! {holder_updated, length(Data)}, - S0#holder{info=EI#etop_info{procinfo=ProcInfo}, sort=Sort}. + Parent ! {holder_updated, array:size(Data)}, + S0#holder{info=ProcInfo, sort=Sort}. change_accum(true, S0) -> S0#holder{accum=true}; -change_accum(false, S0=#holder{info=#etop_info{procinfo=Info}}) -> +change_accum(false, S0=#holder{info=Info}) -> self() ! refresh, - S0#holder{accum=lists:sort(Info)}. + S0#holder{accum=lists:sort(array:to_list(Info))}. handle_update(EI=#etop_info{procinfo=ProcInfo0}, S0=#holder{parent=Parent, sort=Sort=#sort{sort_key=KeyField}}) -> {ProcInfo1, S1} = accum(ProcInfo0, S0), {_SO, ProcInfo} = sort(KeyField, Sort#sort{sort_key=undefined}, ProcInfo1), - Parent ! {holder_updated, length(ProcInfo)}, - S1#holder{info=EI#etop_info{procinfo=ProcInfo}}. + Parent ! {holder_updated, array:size(ProcInfo)}, + S1#holder{info=ProcInfo, etop=EI#etop_info{procinfo=[]}}. accum(ProcInfo, State=#holder{accum=true}) -> {ProcInfo, State}; @@ -532,12 +535,18 @@ accum2([PI|PIs], Old, Acc) -> accum2(PIs, Old, [PI|Acc]); accum2([], _, Acc) -> Acc. +sort(Col, Opt, Table) + when not is_list(Table) -> + sort(Col,Opt,array:to_list(Table)); sort(Col, Opt=#sort{sort_key=Col, sort_incr=Bool}, Table) -> - {Opt#sort{sort_incr=not Bool}, lists:reverse(Table)}; + {Opt#sort{sort_incr=not Bool}, + array:from_list(lists:reverse(Table))}; sort(Col, S=#sort{sort_incr=true}, Table) -> - {S#sort{sort_key=Col}, lists:keysort(col_to_element(Col), Table)}; + {S#sort{sort_key=Col}, + array:from_list(lists:keysort(col_to_element(Col), Table))}; sort(Col, S=#sort{sort_incr=false}, Table) -> - {S#sort{sort_key=Col}, lists:reverse(lists:keysort(col_to_element(Col), Table))}. + {S#sort{sort_key=Col}, + array:from_list(lists:reverse(lists:keysort(col_to_element(Col), Table)))}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -552,40 +561,50 @@ col_to_element(?COL_FUN) -> #etop_proc_info.cf; col_to_element(?COL_MSG) -> #etop_proc_info.mq. get_pids(From, Indices, ProcInfo) -> - Processes = [(lists:nth(I+1, ProcInfo))#etop_proc_info.pid || I <- Indices], + Processes = [(array:get(I, ProcInfo))#etop_proc_info.pid || I <- Indices], From ! {self(), Processes}. get_name_or_pid(From, Indices, ProcInfo) -> Get = fun(#etop_proc_info{name=Name}) when is_atom(Name) -> Name; (#etop_proc_info{pid=Pid}) -> Pid end, - Processes = [Get(lists:nth(I+1, ProcInfo)) || I <- Indices], + Processes = [Get(array:get(I, ProcInfo)) || I <- Indices], From ! {self(), Processes}. - get_row(From, Row, pid, Info) -> Pid = case Row =:= -1 of true -> {error, undefined}; - false -> {ok, get_procinfo_data(?COL_PID, lists:nth(Row+1, Info))} + false -> {ok, get_procinfo_data(?COL_PID, array:get(Row, Info))} end, From ! {self(), Pid}; get_row(From, Row, Col, Info) -> - Data = case Row+1 > length(Info) of + Data = case Row > array:size(Info) of true -> ""; false -> - ProcInfo = lists:nth(Row+1, Info), + ProcInfo = array:get(Row, Info), get_procinfo_data(Col, ProcInfo) end, From ! {self(), observer_lib:to_str(Data)}. get_rows_from_pids(From, Pids0, Info) -> - Res = lists:foldl(fun(Pid, Data = {Ids, Pids}) -> - case index(Pid, Info, 0) of - false -> Data; - Index -> {[Index|Ids], [Pid|Pids]} - end - end, {[],[]}, Pids0), + Search = fun(Idx, #etop_proc_info{pid=Pid}, Acc0={Pick0, {Idxs, Pids}}) -> + case ordsets:is_element(Pid, Pick0) of + true -> + Acc = {[Idx|Idxs],[Pid|Pids]}, + Pick = ordsets:del_element(Pid, Pick0), + case Pick =:= [] of + true -> throw(Acc); + false -> {Pick, Acc} + end; + false -> Acc0 + end + end, + Res = try + {_, R} = array:foldl(Search, {ordsets:from_list(Pids0), {[],[]}}, Info), + R + catch R0 -> R0 + end, From ! {self(), Res}. get_attr(From, Row, Attrs) -> @@ -594,7 +613,3 @@ get_attr(From, Row, Attrs) -> false -> Attrs#attrs.odd end, From ! {self(), Attribute}. - -index(Pid, [#etop_proc_info{pid=Pid}|_], Index) -> Index; -index(Pid, [_|PI], Index) -> index(Pid, PI, Index+1); -index(_, _, _) -> false. -- cgit v1.2.3