diff --git a/src/dynamicCompile/utKvsToBeam.erl b/src/dynamicCompile/utKvsToBeam.erl index 828300e..5dec6c6 100644 --- a/src/dynamicCompile/utKvsToBeam.erl +++ b/src/dynamicCompile/utKvsToBeam.erl @@ -2,6 +2,7 @@ -export([ load/2 + , beamToSrc/1 ]). %% 注意 map类型的数据不能当做key @@ -40,3 +41,16 @@ lookup_clauses([], Acc) -> lists:reverse(lists:flatten([lookup_clause_anon() | Acc])); lookup_clauses([{Key, Value} | T], Acc) -> lookup_clauses(T, [lookup_clause(Key, Value) | Acc]). + +%% 通过beam生成erl文件,生成的beam编译选项必要带debug_info才行 +beamToSrc(Module) -> + case beam_lib:chunks(code:which(Module), [abstract_code]) of + {ok, {_, [{abstract_code, {_, AC}}]}} -> + Code = erl_prettypr:format(erl_syntax:form_list(AC)), + file:write_file(lists:concat([Module, ".erl"]), list_to_binary(Code)), + io:format("build beam:~p to erl:~p success.~n", [Module, Module]); + {error, beam_lib, Reason} -> + io:format("code_gen_erl_file error, reason:~p~n", [Reason]); + _Err -> + io:format("code_gen_erl_file error, reason:~p~n", [_Err]) + end. diff --git a/src/srvNodeMgr/main.erl b/src/srvNodeMgr/main.erl new file mode 100644 index 0000000..ba26b31 --- /dev/null +++ b/src/srvNodeMgr/main.erl @@ -0,0 +1,149 @@ +-module(main). + +-export([ + server_start/0, + server_stop/0, + server_stop/1, + is_running/0, + is_running/1, + reload/0, + info/0, + psl/0, + psl/1, + psl/2, + get_info/0 +]). + +-define(SERVER_APPS, [sasl, crypto, inets, ranch, os_mon, hot]). + +server_start()-> + ok = application:load(hot), + app_misc:init(), + ok = start_applications(?SERVER_APPS). + +%% 加载更新 +reload() -> + reloader:reload_all(). + +%%停止游戏服务器 +server_stop() -> + server_stop(30). + +is_running() -> + is_running(node()). + +is_running(Node) -> + node_misc:is_process_running(Node, server). + +%%停止游戏服务器 +server_stop(_SleepSeconds) -> + app_misc:pause_accept(), + stop_applications(?SERVER_APPS), + ok. + +info() -> + io:format( "abormal termination: + ~n Scheduler id: ~p + ~n Num scheduler: ~p + ~n Process count: ~p + ~n Process limit: ~p + ~n Memory used by erlang processes: ~p + ~n Memory allocated by erlang processes: ~p + ~n The total amount of memory allocated: ~p + ~n", + get_info()), + ok. +get_info() -> + SchedId = erlang:system_info(scheduler_id), + SchedNum = erlang:system_info(schedulers), + ProcCount = erlang:system_info(process_count), + ProcLimit = erlang:system_info(process_limit), + ProcMemUsed = erlang:memory(processes_used), + ProcMemAlloc = erlang:memory(processes), + MemTot = erlang:memory(total), + [SchedId, SchedNum, ProcCount, ProcLimit, + ProcMemUsed, ProcMemAlloc, MemTot]. + +psl() -> + psl(100). + +psl(Num) -> + lists:foldl( + fun(P, true)-> + case erlang:process_info(P, message_queue_len) of + {message_queue_len, Count} when Count > Num -> + print_process_info(P), + false; + _ -> + true + end; + (_, false) -> + false + end, true, erlang:processes()). + +psl(ProcessPid, Num) -> + case erlang:process_info(ProcessPid, message_queue_len) of + {message_queue_len, Count} when Count > Num -> + print_process_info(ProcessPid); + _ -> + ok + end. + +print_process_info(P) -> + io:format("~n~n=====process info===~n" + "~p~n~p~n~p~n~p~n~p~n~p~n~p~n~p~n~p~n~p~n~n~n", + [P, + erlang:process_info(P, registered_name), + erlang:process_info(P, current_function), + erlang:process_info(P, message_queue_len), + erlang:process_info(P, status), + erlang:process_info(P, suspending), + erlang:process_info(P, last_calls), + erlang:process_info(P, links), + erlang:process_info(P, dictionary), + erlang:process_info(P, current_stacktrace) + ]). + +%%############辅助调用函数############## +manage_applications(Iterate, Do, Undo, SkipError, ErrorTag, Apps) -> + Iterate(fun(App, Acc) -> + case Do(App) of + ok -> [App | Acc];%合拢 + {error, {SkipError, _}} when is_atom(SkipError) -> + Acc; + {error, {Error, Reason}} when is_list(SkipError) -> + case lists:member(Error, SkipError) of + true -> + Acc; + false -> + io:format( + "App ~p, Reason ~p~n", [App, Reason]), + lists:foreach(Undo, Acc), + throw({error, {ErrorTag, App, Reason}}) + end; + {error, Reason} -> + io:format("App ~p, Reason ~p~n", [App, Reason]), + lists:foreach(Undo, Acc), + throw({error, {ErrorTag, App, Reason}}) + end + end, [], Apps), + ok. + +start_applications(Apps) -> + manage_applications(fun lists:foldl/3, + fun application:start/1, + fun application:stop/1, + [already_started, cannot_start_application], + cannot_start_application, + Apps). + +stop_applications(Apps) -> + io:format("stop_applications stopping.~n",[]), + manage_applications(fun lists:foldr/3, + fun application:stop/1, + fun application:start/1, + not_started, + cannot_stop_application, + Apps). + + diff --git a/src/srvNodeMgr/node_misc.erl b/src/srvNodeMgr/node_misc.erl new file mode 100644 index 0000000..bfeee39 --- /dev/null +++ b/src/srvNodeMgr/node_misc.erl @@ -0,0 +1,59 @@ +-module(node_misc). + +-export([names/1, make/1, parts/1, cookie_hash/0, + is_running/2, is_process_running/2]). + +-define(EPMD_TIMEOUT, 30000). + +names(Hostname) -> + Self = self(), + Ref = make_ref(), + {Pid, MRef} = spawn_monitor( + fun () -> + Self ! {Ref, net_adm:names(Hostname)} + end), + timer:exit_after(?EPMD_TIMEOUT, Pid, timeout), + receive + {Ref, Names} -> + erlang:demonitor(MRef, [flush]), + Names; + {'DOWN', MRef, process, Pid, Reason} -> + {error, Reason} + end. + +make({Prefix, Suffix}) -> + list_to_atom(lists:append([Prefix, "@", Suffix])); +make(NodeStr) -> + make(parts(NodeStr)). + +parts(Node) when is_atom(Node) -> + parts(atom_to_list(Node)); +parts(NodeStr) -> + case lists:splitwith(fun (E) -> E =/= $@ end, NodeStr) of + {Prefix, []} -> + {_, Suffix} = parts(node()), + {Prefix, Suffix}; + {Prefix, Suffix} -> + {Prefix, tl(Suffix)} + end. + +cookie_hash() -> + base64:encode_to_string(erlang:md5(atom_to_list(erlang:get_cookie()))). + +is_running(Node, Application) -> + case rpc:call(Node, app_utils, which_applications, []) of + {badrpc, _} -> + false; + Apps -> + proplists:is_defined(Application, Apps) + end. + +is_process_running(Node, Process) -> + case rpc:call(Node, erlang, whereis, [Process]) of + {badrpc, _} -> + false; + undefined -> + false; + P when is_pid(P) -> + true + end. diff --git a/src/srvNodeMgr/reloader.erl b/src/srvNodeMgr/reloader.erl new file mode 100644 index 0000000..d549405 --- /dev/null +++ b/src/srvNodeMgr/reloader.erl @@ -0,0 +1,165 @@ +%% @copyright 2007 Mochi Media, Inc. +%% @author Matthew Dempsky +%% +%% @doc Erlang module for automatically reloading modified modules +%% during development. + +-module(reloader). +-author("Matthew Dempsky "). + +-include_lib("kernel/include/file.hrl"). + +-behaviour(gen_server). + +-export([start/0, start_link/0]). +-export([stop/0]). +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +-export([all_changed/0]). +-export([is_changed/1]). +-export([reload_modules/1]). +-export([reload_all/0]). + +-record(state, {last, tref}). + +%% External API + +%% @spec start() -> ServerRet +%% @doc Start the reloader. +start() -> + gen_server:start({local, ?MODULE}, ?MODULE, [], []). + +%% @spec start_link() -> ServerRet +%% @doc Start the reloader. +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +%% @spec stop() -> ok +%% @doc Stop the reloader. +stop() -> + gen_server:call(?MODULE, stop). + +%% gen_server callbacks +%% -define(RERODER_CHECK_TIME, 5000). + +%% @spec init([]) -> {ok, State} +%% @doc gen_server init, opens the server in an initial state. +init([]) -> + %% {ok, TRef} = timer:send_interval(timer:seconds(1), doit), + %% TimerRef = erlang:send_after(?RERODER_CHECK_TIME, self(), doit), + %% tref = TimerRef}}. + {ok, #state{last = stamp()}}. + + +%% @spec handle_call(Args, From, State) -> tuple() +%% @doc gen_server callback. +handle_call(stop, _From, State) -> + {stop, shutdown, stopped, State}; +handle_call(_Req, _From, State) -> + {reply, {error, badrequest}, State}. + +%% @spec handle_cast(Cast, State) -> tuple() +%% @doc gen_server callback. +%% @spec handle_info(Info, State) -> tuple() +%% @doc gen_server callback. +handle_cast(doit, State) -> + error_logger:info_msg("reloader do reload ... ~n", []), + %% TimerRef = erlang:send_after(?RERODER_CHECK_TIME, self(), doit), + Now = stamp(), + try + _ = doit(State#state.last, Now), + %% tref = TimerRef + error_logger:info_msg("reloader done ... ~n", []), + {noreply, State#state{last = Now}} + catch + _:R -> + error_logger:error_msg( + "reload failed R:~w Stack:~p~n", [R, erlang:get_stacktrace()]), + %% reloader failed, no state update + {noreply, State} + end; +handle_cast(_Req, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +%% @spec terminate(Reason, State) -> ok +%% @doc gen_server termination callback. +terminate(_Reason, _State) -> + %% erlang:cancel_timer(State#state.tref), + %% {ok, cancel} = timer:cancel(State#state.tref), + ok. + +%% @spec code_change(_OldVsn, State, _Extra) -> State +%% @doc gen_server code_change callback (trivial). +code_change(_Vsn, State, _Extra) -> + {ok, State}. + +%% @spec reload_modules([atom()]) -> [{module, atom()} | {error, term()}] +%% @doc code:purge/1 and code:load_file/1 the given list of modules in order, +%% return the results of code:load_file/1. +reload_modules(Modules) -> + [begin code:purge(M), code:load_file(M) end || M <- Modules]. + +%% @spec all_changed() -> [atom()] +%% @doc Return a list of beam modules that have changed. +all_changed() -> + [M || {M, Fn} <- code:all_loaded(), is_list(Fn), is_changed(M)]. + +%% @spec reload_all() -> [atom()] +reload_all() -> + gen_server:cast(?MODULE, doit). + +%% @spec is_changed(atom()) -> boolean() +%% @doc true if the loaded module is a beam with a vsn attribute +%% and does not match the on-disk beam file, returns false otherwise. +is_changed(M) -> + try + module_vsn(M:module_info()) =/= module_vsn(code:get_object_code(M)) + catch _:_ -> + false + end. + +%% Internal API + +module_vsn({M, Beam, _Fn}) -> + {ok, {M, Vsn}} = beam_lib:version(Beam), + Vsn; +module_vsn(L) when is_list(L) -> + {_, Attrs} = lists:keyfind(attributes, 1, L), + {_, Vsn} = lists:keyfind(vsn, 1, Attrs), + Vsn. + +doit(From, To) -> + [case file:read_file_info(Filename) of + {ok, #file_info{mtime = Mtime}} when Mtime >= From, Mtime < To -> + reload(Module); + {ok, _} -> + unmodified; + {error, enoent} -> + %% The Erlang compiler deletes existing .beam files if + %% recompiling fails. Maybe it's worth spitting out a + %% warning here, but I'd want to limit it to just once. + gone; + {error, Reason} -> + error_logger:error_msg("Error reading ~s's file info: ~p~n", + [Filename, Reason]), + error + end || {Module, Filename} <- code:all_loaded(), is_list(Filename)]. + +reload(Module) -> + error_logger:info_msg("Reloading ~p ...", [Module]), + code:purge(Module), + case code:load_file(Module) of + {module, Module} -> + error_logger:info_msg("reload ~w ok.~n", [Module]), + reload; + {error, Reason} -> + error_logger:error_msg("reload fail: ~p.~n", [Reason]), + error + end. + + +stamp() -> + erlang:localtime(). + diff --git a/src/srvNodeMgr/server_control_main.erl b/src/srvNodeMgr/server_control_main.erl new file mode 100644 index 0000000..be78325 --- /dev/null +++ b/src/srvNodeMgr/server_control_main.erl @@ -0,0 +1,145 @@ +-module(server_control_main). + +-export([start/0]). + +-define(RPC_TIMEOUT, infinity). + +commands_desc() -> + [{"stop", "停止游戏服务器进程"}, + {"stop_all", "停止游戏集群进程"}, + {"stop_app", "关闭server application"}, + {"start_app", "打开server application"}, + {"cluster_status", "集群状态"}]. +opt_spec_list() -> + Node = case get(nodename) of + undefined -> + throw(not_nodename); + V -> + V + end, + [ + {help, $h, "help", undefined, "显示帮助,然后退出"}, + {node, undefined, "node", {atom, Node}, "管理节点"} + ]. +usage() -> + getopt:usage(opt_spec_list(), "server_ctl", " []", commands_desc()), + err_misc:quit(1). +parse_arguments(CmdLine) -> + case getopt:parse(opt_spec_list(), CmdLine) of + {ok, {Opts, [Command | Args]}} -> + {ok, {list_to_atom(Command), Opts, Args}}; + {ok, {_Opts, []}} -> + no_command; + Error -> + io:format("Error ~p~n", [Error]), + no_command + end. + +start() -> + {ok, [[NodeStr|_]|_]} = init:get_argument(nodename), + put(nodename, list_to_atom(NodeStr)), + {Command, Opts, Args} = + case parse_arguments(init:get_plain_arguments()) of + {ok, Res} -> + Res; + no_command -> + usage() + end, + Node = proplists:get_value(node, Opts), + net_adm:ping(Node), + timer:sleep(1000), %% wait auto find node + %% The reason we don't use a try/catch here is that rpc:call turns + %% thrown errors into normal return values + % io:format("Opts ~p~n", [Opts]), + case catch action(Command, Node, Args, Opts) of + ok -> + io:format("done.~n", []), + quit(0); + {ok, Info} -> + io:format("done (~p).~n", [Info]), + quit(0); + Other -> + io:format("other result ~p~n", [Other]), + quit(2) + end. + +action(info, Node, _Args, _Opts) -> + io:format("System info for Node ~p~n", [Node]), + Res = call(Node, {main, get_info, []}), + io:format( " ~n Scheduler id: ~p + ~n Num scheduler: ~p + ~n Process count: ~p + ~n Process limit: ~p + ~n Memory used by erlang processes: ~p + ~n Memory allocated by erlang processes: ~p + ~n The total amount of memory allocated: ~p + ~n", + Res), + ok; +action(backup, Node, _Args, _Opts) -> + case call(Node, {app_misc, backup, []}) of + {error, Msg} -> + io:format("~s~n", [Msg]); + {ok, FileName} -> + io:format("backup file:~s~n", [FileName]), + io:format("backup file to remote ......~n", []), + Result = os:cmd("bash copy_to_remote.sh " ++ FileName), + io:format("~s~n", [Result]) + end, + ok; + +action(pause_accept, Node, _Args, _Opts) -> + io:format("Pause accept new client ~p~n", [Node]), + call(Node, {app_misc, pause_accept, []}), + ok; +action(resume_accept, Node, _Args, _Opts) -> + io:format("Resume accept new client ~p~n", [Node]), + call(Node, {app_misc, resume_accept, []}), + ok; +action(accept_state, Node, _Args, _Opts) -> + Res = call(Node, {app_misc, can_accept_new, []}), + io:format("Node ~p accept state:~p~n ", [Node, Res]), + ok; +action(reload, Node, _Args, _Opts) -> + io:format("Reloading node ~p~n", [Node]), + call(Node, {main, reload, []}); +action(stop_all, MasterNode, _Args, _Opts) -> + io:format("Stopping and halting all node~n", []), + PidMRefs = [{spawn_monitor(fun() -> + call(Node, {main, stop_and_halt, [5]}) + end), Node} + || Node <- nodes() -- [MasterNode]], + [receive + {'DOWN', MRef, process, _, normal} -> + ok; + {'DOWN', MRef, process, _, Reason} -> + io:format("Node ~p Error, Reason ~p", [Node, Reason]) + end || {{_Pid, MRef}, Node} <- PidMRefs], + call(MasterNode, {main, stop_and_halt, [5]}), + ok; +action(stop, Node, _Args, _Opts) -> + io:format("Stopping and halting node ~p~n", [Node]), + call(Node, {main, stop_and_halt, [5]}); +action(Command, _Node, Args, Opts) -> + io:format("Command: ~p Args: ~p Opts: ~p~n", [Command, Args, Opts]), + invalid_command. + +call(Node, {Mod, Fun, Args}) -> + %%rpc_call(Node, Mod, Fun, lists:map(fun list_to_binary/1, Args)). + rpc_call(Node, Mod, Fun, Args). + +rpc_call(Node, Mod, Fun, Args) -> + rpc:call(Node, Mod, Fun, Args, ?RPC_TIMEOUT). + + +quit(Status) -> + case os:type() of + {unix, _} -> + halt(Status); + {win32, _} -> + init:stop(Status), + receive + after infinity -> + ok + end + end. diff --git a/src/srvNodeMgr/u.erl b/src/srvNodeMgr/u.erl new file mode 100644 index 0000000..0788b50 --- /dev/null +++ b/src/srvNodeMgr/u.erl @@ -0,0 +1,151 @@ +%%---------------------------------------------------- +%% Erlang模块热更新到所有线路(包括server的回调函数,如果对state有影响时慎用) +%% +%% 检查:u:c() %% 列出前5分钟内编译过的文件 +%% u:c(N) %% 列出前N分钟内编译过的文件 +%% +%% 更新:u:u() %% 更新前5分钟内编译过的文件 +%% u:u(N) %% 更新前N分钟内编译过的文件 +%% u:u([mod_xx, ...]) %% 指定模块(不带后缀名) +%% u:u(m) %% 编译并加载文件 +%% +%% Tips: u - update, c - check +%% +%% @author rolong@vip.qq.com +%%---------------------------------------------------- + +-module(u). +-compile(export_all). +-include_lib("kernel/include/file.hrl"). + +c() -> + c(5). +c(S) when is_integer(S) -> + c:cd("../ebin"), + case file:list_dir(".") of + {ok, FileList} -> + Files = get_new_file(FileList, S * 60), + info("---------check modules---------~n~w~n=========check modules=========", [Files]); + Any -> info("Error Dir: ~w", [Any]) + end; +c([S]) when is_atom(S) -> + S1 = tool:to_integer(tool:to_list(S)), + case is_integer(S1) of + true -> + c:cd("../ebin"), + case file:list_dir(".") of + {ok, FileList} -> + Files = get_new_file(FileList, S * 60), + info("---------check modules---------~n~w~n=========check modules=========", [Files]); + Any -> info("Error Dir: ~w", [Any]) + end; + _ -> + info("ERROR======> Badarg ~p/~p ~n", [S, S1]) + end; +c(S) -> info("ERROR======> Badarg ~p ~n", [S]). + +admin()-> + spawn(fun()->u(m) end), + ok. + +u() -> + u(5). +u(m) -> + StartTime = util:unixtime(), + info("----------makes----------", []), + c:cd("../"), + make:all(), + c:cd("ebin"), + EndTime = util:unixtime(), + Time = EndTime - StartTime, + info("Make Time : ~w s", [Time]), + u(Time / 60); +u(S) when is_number(S) -> + case file:list_dir(".") of + {ok, FileList} -> + Files = get_new_file(FileList, util:ceil(S * 60) + 3), + load(Files); +%% AllZone = mod_node_interface:server_list(), +%% info("---------modules---------~n~w~n----------nodes----------", [Files]), +%% loads(AllZone, Files); + Any -> info("Error Dir: ~w", [Any]) + end; +u(Files) when is_list(Files) -> + load(Files); +%% AllZone = mod_node_interface:server_list(), +%% info("---------modules---------~n~w~n----------nodes----------", [Files]), +%% loads(AllZone, Files); +u(_) -> info("ERROR======> Badarg", []). + +%% m(['src/data/*','src/lib/lib_goods.erl']) +m(Files) when is_list(Files) -> + StartTime = util:unixtime(), + info("----------makes----------~n~w~n", [Files]), + c:cd("../"), + Res = make:files(Files, [debug_info,{i, "include"},{outdir, "ebin"}]), + c:cd("ebin"), + EndTime = util:unixtime(), + Time = EndTime - StartTime, + info("Make Time : ~w s", [Time]), + Res. + +info(V) -> + info(V, []). +info(V, P) -> + io:format(V ++ "~n", P). + +%% 更新到所有线路,暂时处理单节点的情况 +%% loads([], _Files) -> ok; +%% loads([H | T], Files) -> +%% info("[~w]", [H#t_server_node.node]), +%% rpc:cast(H#t_server_node.node, u, load, [Files]), +%% loads(T, Files). + +get_new_file(Files, S) -> + get_new_file(Files, S, []). +get_new_file([], _S, Result) -> Result; +get_new_file([H | T], S, Result) -> + NewResult = case string:tokens(H, ".") of + [Left, Right] when Right =:= "beam" -> + case file:read_file_info(H) of + {ok, FileInfo} -> + Now = calendar:local_time(), + case calendar:time_difference(FileInfo#file_info.mtime, Now) of + {Days, Times} -> + Seconds = calendar:time_to_seconds(Times), + case Days =:= 0 andalso Seconds < S of + true -> + FileName = list_to_atom(Left), + [FileName | Result]; + false -> Result + end; + _ -> Result + end; + _ -> Result + end; + _ -> Result + end, + get_new_file(T, S, NewResult). + +load([]) -> ok; +load([FileName | T]) -> + c:l(FileName), + info("loaded: ~w", [FileName]), + load(T). +% case code:soft_purge(FileName) of +% true -> +% case code:load_file(FileName) of +% {module, _} -> +% info("loaded: ~w", [FileName]), +% ok; +% %% info("loaded: ~w", [FileName]); +% {error, What} -> info("ERROR======> loading: ~w (~w)", [FileName, What]) +% end; +% false -> info("ERROR======> Processes lingering : ~w [zone ~w] ", [FileName, srv_kernel:zone_id()]) +% end, +% load(T). + + +a() -> + c(), + u(). \ No newline at end of file diff --git a/src/startStop/utAppStart.erl b/src/srvNodeMgr/utAppStart.erl similarity index 100% rename from src/startStop/utAppStart.erl rename to src/srvNodeMgr/utAppStart.erl diff --git a/src/startStop/utSrvManager.erl b/src/srvNodeMgr/utSrvManager.erl similarity index 100% rename from src/startStop/utSrvManager.erl rename to src/srvNodeMgr/utSrvManager.erl diff --git a/src/startStop/utStopSrv1.escript b/src/srvNodeMgr/utStopSrv1.escript similarity index 100% rename from src/startStop/utStopSrv1.escript rename to src/srvNodeMgr/utStopSrv1.escript diff --git a/src/startStop/utStopSrv2.escript b/src/srvNodeMgr/utStopSrv2.escript similarity index 100% rename from src/startStop/utStopSrv2.escript rename to src/srvNodeMgr/utStopSrv2.escript diff --git a/src/testCase/utTestDS.erl b/src/testCase/utTestDS.erl new file mode 100644 index 0000000..937864a --- /dev/null +++ b/src/testCase/utTestDS.erl @@ -0,0 +1,35 @@ +-module(utTestDS). +-compile([export_all, nowarn_unused_function, nowarn_unused_vars, nowarn_export_all]). + +%% 用于测试erlang各种数据结构 读写遍历等操作的效率 +%%  lists ,maps 和record是erlang最为常用的数据结构,lists使用方便简单,maps则查询高效,record则需要预定义, +%% 可扩展性差,各有各的优。本文做一下lists和maps的性能对比(再对比一下dict),代码如下(record操作不便则不做比较)。 + +%%通过注释部分代码做以下测试 +%%timer:tc(lib_test, test_struct, [10000,#{}]). +%%timer:tc(lib_test, test_struct, [10000,[]]). +test_struct(0, R) -> + Fun = fun({K, V}) -> K + 1, V + 1 end, lists:foreach(Fun, R), %%遍历测试 + Fun = fun(K, V) -> K + 1, V + 1 end, maps:map(Fun, R), + ok; +test_struct(Num, R) -> + NewR = [{Num, Num} | R], lists:keyfind(5000, 1, NewR), %%插入查询测试 + NewR = R#{Num=>Num}, maps:get(5000, NewR, 0), + test_struct(Num - 1, NewR). +%% 做10000次的插入查询测试结果: +%% +%%     lists 50736微秒 +%%     maps 4670微秒 +%%     dict 60236微秒 +%% 做10000次的遍历结果: +%% +%%     lists 523微秒 +%%     maps 8337微秒 +%%     dict 4426微秒 +%% 对比总结: +%% +%%     对比测试数据maps在查询性能上比lists高10倍以上, 而在遍历上lists则更优。对于频繁插入和查询的数据,maps是最佳的选择, +%% lists则适用于广播列表之类需要遍历的数据。除此之外,个人觉得在使用上lists 比maps更为方便,因为lists模块提供了大量实用的函数, +%% 单单在排序上,maps的实用性就不如lists了,所以在数据结构选择上就需要多多斟酌。另外record在查询上使用的是模式匹配,性能只会更高, +%% 但需要提前定义字段,可扩展性差,在热更这块有不少坑,maps也可以用模式匹配查询,但也要确保key值存在,不然就nomatch, +%% 但整体上maps更优于record,故建议用maps替代record。 diff --git a/src/testCase/utTestMd5.erl b/src/testCase/utTestMd5.erl index 8d71010..1611589 100644 --- a/src/testCase/utTestMd5.erl +++ b/src/testCase/utTestMd5.erl @@ -100,6 +100,21 @@ tt7(N, Hex) -> String, tt7(N - 1, Hex). +u4(0, Fun) -> + ?MODULE:Fun(); +u4(N, Fun) -> + ?MODULE:Fun(), + u4(N - 1, Fun). + +uuid() -> + erlang:md5(term_to_binary({erlang:system_time(nanosecond), rand:uniform(134217727), make_ref()})). +uuid2() -> + term_to_binary({erlang:system_time(nanosecond), rand:uniform(134217727), make_ref()}). + +get_uuid() -> + <<(crypto:strong_rand_bytes(8))/bytes, + (erlang:term_to_binary(erlang:now()))/bytes>>. + u1(0) -> crypto:strong_rand_bytes(16); u1(N) -> diff --git a/src/testCase/utTestPerformance.erl b/src/testCase/utTestPerformance.erl index f2ff49a..7924738 100644 --- a/src/testCase/utTestPerformance.erl +++ b/src/testCase/utTestPerformance.erl @@ -178,33 +178,48 @@ st3() -> st4() -> size(<<"fdfdfdd:fdffd:\rn\n:fdfd fd df df dfddfdf">>). --record(state, { - lSock - , ref - , cliMod - , sockMod -}). - -h1(0, Fun) -> - ok; -h1(N, Fun) -> - Ref1 = make_ref(), - Ref2 = make_ref(), - ?MODULE:Fun({inet_async, Ref1, Ref2, {432423,3443,55}}, #state{lSock = Ref1, ref = Ref1}), - h1(N - 1, Fun). - -hm1({inet_async, LSock, Ref, Msg}, #state{lSock = LSock, ref = Ref, cliMod = _CliMod, sockMod = _SockMod} = State) -> +gm(0, Fun) -> ok; -hm1({inet_async, LSock, Ref, Msg}, #state{lSock = LSock1, ref = Ref1, cliMod = _CliMod, sockMod = _SockMod} = State) -> - false. - -hm2({inet_async, LSock, Ref, Msg}, #state{lSock = LSock1, ref = Ref1, cliMod = _CliMod, sockMod = _SockMod} = State) -> - case LSock == LSock1 andalso Ref == Ref1 of - true -> - ok; +gm(N, Fun) -> + [?MODULE:Fun(M) || M <- [1,2,3,4,5,6,7,8,9,10,11,12]], + gm(N - 1, Fun). + +%% 这个更快 +getMonth(1) -> + <<"Jan">>; +getMonth(2) -> + <<"Feb">>; +getMonth(3) -> + <<"Mar">>; +getMonth(4) -> + <<"Apr">>; +getMonth(5) -> + <<"May">>; +getMonth(6) -> + <<"Jun">>; +getMonth(7) -> + <<"Jul">>; +getMonth(8) -> + <<"Aug">>; +getMonth(9) -> + <<"Sep">>; +getMonth(10) -> + <<"Oct">>; +getMonth(11) -> + <<"Nov">>; +getMonth(12) -> + <<"Dec">>. + +getMonth2(Month) -> + element(Month,{<<"Jan">>,<<"Feb">>,<<"Mar">>,<<"Apr">>,<<"May">>,<<"Jun">>,<<"Jul">>,<<"Aug">>,<<"Sep">>,<<"Oct">>,<<"Nov">>,<<"Dec">>}). + +-define(Month, #{1 => <<"Jan">>, 2 => <<"Feb">>, 3 => <<"Mar">>, 4 => <<"Apr">>, 5 => <<"May">>, 6 => <<"Jun">>, 7 => <<"Jul">>, 8 => <<"Aug">>, 9 => <<"Sep">>, 10 => <<"Oct">>, 11 => <<"Nov">>, 12 => <<"Dec">>}). +getMonth3(Month) -> + case ?Month of + #{Month := MonthStr} -> + MonthStr; _ -> - false + <<"">> end. -