%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. %% %% %CopyrightEnd% %% %%%------------------------------------------------------------------- %%% @author Rickard Green %%% @doc %%% An ETS benchmark. %%% %%% Probably not the most beautiful Erlang code you've seen. It was %%% written in a bit of a hurry. %%% %%% @end %%% Created : 14 Oct 2010 by Rickard Green %%%------------------------------------------------------------------- -module(etsb). -define(NO_TEST_RUNS, 5). -define(TSIZE, (100 * 1000 *1000)). -export([go/1]). write_elog(Res, Name, XOpts, ReadUntil, Count, Loops, SC) -> S = erlang:system_info(schedulers), FileName = Name ++ "-" ++ opts_str(XOpts) ++ "-" ++ writers(ReadUntil, Count, Loops) ++ case SC of false -> ""; _ -> "-sw" ++ writers(ReadUntil, SC, Loops) end, io:format("~s ~p ~p~n", [FileName, S, Res]), {ok, IOD} = file:open(FileName++".elog", [write,append]), io:format(IOD, "{~p, ~p}.~n", [S, Res]), file:close(IOD). write_avg_log(Results, Name, XOpts, ReadUntil, Count, Loops, SC) -> S = erlang:system_info(schedulers), Res = lists:sum(Results)/length(Results), FileName = Name ++ "-" ++ opts_str(XOpts) ++ "-" ++ writers(ReadUntil, Count, Loops) ++ case SC of false -> ""; _ -> "-sw" ++ writers(ReadUntil, SC, Loops) end, io:format("~s-avg ~p ~p~n", [FileName, S, Res]), {ok, IOD} = file:open(FileName, [write,append]), io:format(IOD, "~p ~p~n", [S, Res]), file:close(IOD). opts_str([]) -> "def"; opts_str([{write_concurrency, true}]) -> "wc"; opts_str([{read_concurrency, true}]) -> "rc"; opts_str([{read_concurrency, true},{write_concurrency, true}]) -> "wrc"; opts_str([{write_concurrency, true},{read_concurrency, true}]) -> "wrc". writers(true, X, X) -> "0"; writers(true, Count, _) -> integer_to_list(round(1/Count*100)); writers(false, X, X) -> "100"; writers(false, Count, _) -> integer_to_list(100 - round(1/Count*100)). put_data() -> put(?MODULE, {"here are some", data, "to store", make_ref()}). get_data() -> get(?MODULE). get_ets_opts([]) -> []; get_ets_opts([$- | Rest]) -> get_ets_opts(Rest); get_ets_opts([$d, $e, $f | Rest]) -> [[] | get_ets_opts(Rest)]; get_ets_opts([$r, $c | Rest]) -> [[{read_concurrency, true}] | get_ets_opts(Rest)]; get_ets_opts([$w, $c | Rest]) -> [[{write_concurrency, true}] | get_ets_opts(Rest)]; get_ets_opts([$w, $r, $c | Rest]) -> [[{read_concurrency, true},{write_concurrency, true}] | get_ets_opts(Rest)]; get_ets_opts([$r, $w, $c | Rest]) -> [[{read_concurrency, true},{write_concurrency, true}] | get_ets_opts(Rest)]. init(T, From, To) when From < To -> ets:insert(T, {From, From, From}), init(T, From+1, To); init(_, _, _) -> ok. ets_loop(T)-> receive {lease, P} -> ets:give_away(T, P, girl), ets_loop(T); {'ETS-TRANSFER',T, P, girl} -> P!{done, self()}, ets_loop(T); bang -> die; _Msg->io:format("unk msg ~p", [_Msg]) end. mon_size_loop(T, P)-> receive bang->die after 5000-> X= ets:info(T, size), io:format("ets current size[~p], ips[~p]~n",[X, (X-P)/5000000]), mon_size_loop(T, X) end. go([Name, LoopsStr, ReadOnlyStr, EtsOpts, TblSizeStr]) -> Loops = list_to_integer(LoopsStr), ReadOnly = list_to_atom(ReadOnlyStr), Procs = 1000, TblSize = list_to_integer(TblSizeStr), lists:foreach(fun (XOpts) -> {ETSP, TM} = spawn_monitor(fun () -> T=ets:new(x, [public | XOpts]), Parent = self(), S = erlang:system_info(schedulers), Seg = TblSize div S, Rem = TblSize rem S, io:format("init workers:~p, seg:~p, rem:~p~n", [S, Seg, Rem]), io:format(":",[]), Start = now(), F = fun (I) -> spawn(fun () -> init(T, (I-1)*Seg, I * Seg), %%io:format("init w:~p, seg:~p~n", [init, self(), I]), Parent!{init, self(), I} end) end, SP = spawn(fun()-> mon_size_loop(T, 0) end), [F(I) || I<-lists:seq(1, S)], init(T, TblSize - Rem, TblSize), repeat_list(fun()->receive {init, _, _} -> ok end end, S), Stop = now(), Res = timer:now_diff(Stop, Start)/1000000, SP!bang, io:format(") size[~p], time[~ps]~n",[ets:info(T, size), Res]), ets_loop(T) end), run(Name, XOpts, true, Loops, Loops, Procs, false, ETSP), case ReadOnly of true -> ok; _ -> run(Name, XOpts, true, Loops, Loops, Procs, 100, ETSP), run(Name, XOpts, true, 100, Loops, Procs, false, ETSP), run(Name, XOpts, true, 2, Loops, Procs, false, ETSP), run(Name, XOpts, false, Loops, Loops, Procs, false, ETSP), ok end, ETSP!bang, %% io:format("~p bang!~n", [ETSP]), receive {'DOWN', TM, process, ETSP, _} -> ok end, die end, get_ets_opts(EtsOpts) ), ok. ops(_T, _UW, _N, _C, _SC, 0) -> ok; ops(T, UW, N, C, SC, Tot) when N >= ?TSIZE -> ops(T, UW, 0, C, SC, Tot); ops(T, UW, N, 0, SC, Tot) -> case UW of true -> true = ets:insert(T, {N, Tot, get_data()}); false -> [{N, _, _}] = ets:lookup(T, N) end, ops(T, UW, N+1, SC, SC, Tot-1); ops(T, UW, N, C, SC, Tot) -> case UW of false -> true = ets:insert(T, {N, Tot, get_data()}); true -> [{N, _, _}] = ets:lookup(T, N) end, ops(T, UW, N+1, C-1, SC, Tot-1). run(Name, XOpts, UW, C, N, NP, SC, ETSP) -> receive after 100 -> ok end, {TP, TM} = spawn_monitor(fun () -> RL = repeat_list(fun () -> Caller = self(), T = fun () -> Parent = self(), put_data(), ETSP!{lease, self()}, T=receive {'ETS-TRANSFER',Tab, ETSP, girl} -> Tab end, Ps0 = repeat_list(fun () -> spawn_link(fun () -> put_data(), receive go -> ok end, ops(T, UW, N, C, C, N), Parent ! {done, self()}, receive after infinity -> ok end end) end, NP - case SC of false -> 0; _ -> 1 end), Ps = case SC of false -> Ps0; _ -> [spawn_link(fun () -> put_data(), receive go -> ok end, ops(T, UW, N, SC, SC, N), Parent ! {done, self()}, receive after infinity -> ok end end) | Ps0] end, Start = now(), lists:foreach(fun (P) -> P ! go end, Ps), lists:foreach(fun (P) -> receive {done, P} -> ok end end, Ps), Stop = now(), lists:foreach(fun (P) -> unlink(P), exit(P, bang), M = erlang:monitor(process, P), receive {'DOWN', M, process, P, _} -> ok end end, Ps), Res = timer:now_diff(Stop, Start)/1000000, write_elog(Res, Name, XOpts, UW, C, N, SC), Caller ! {?MODULE, self(), Res}, ets:give_away(T, ETSP, girl), receive {done, ETSP} -> ok end, die end, TP = spawn_link(T), receive {?MODULE, TP, Res} -> Res end end, ?NO_TEST_RUNS), write_avg_log(RL, Name, XOpts, UW, C, N, SC) end), receive {'DOWN', TM, process, TP, _} -> ok end. repeat_list(Fun, N) -> repeat_list(Fun, N, []). repeat_list(_Fun, 0, Acc) -> Acc; repeat_list(Fun, N, Acc) -> repeat_list(Fun, N-1, [Fun()|Acc]).