%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2002-2017. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% -module(estone_SUITE). %% Test functions -export([all/0, suite/0, groups/0, estone/1, estone_bench/1, pgo/0]). %% Internal exports for EStone tests -export([lists/1, msgp/1, msgp_medium/1, msgp_huge/1, pattern/1, trav/1, port_io/1, large_dataset_work/1, large_local_dataset_work/1,mk_big_procs/1,big_proc/0, very_big/1, alloc/1, bif_dispatch/1, binary_h/1,echo/1, ets/1, generic/1,req/2,gserv/4,handle_call/3, int_arith/1, float_arith/1, fcalls/1,remote0/1,remote1/1,app0/1,app1/1, timer/1, links/1,lproc/1, run_micro/3,p1/1,ppp/3,macro/2,micros/0]). -ifndef(PGO). -include_lib("common_test/include/ct_event.hrl"). -endif. %% EStone defines -define(TOTAL, (3000 * 1000 * 100)). %% 300 secs -define(BIGPROCS, 2). -define(BIGPROC_SIZE, 50). -define(STONEFACTOR, 31000000). %% Factor to make the reference %% implementation to make 1000 TS_ESTONES. -record(micro, {function, %% The name of the function implementing the micro weight, %% How important is this in typical applications ?? loops = 100,%% initial data tt1, %% time to do one round str}). %% Header string suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 4}}]. all() -> [estone]. groups() -> [{estone_bench, [{repeat,50}],[estone_bench]}]. %% EStone Test estone(Config) when is_list(Config) -> DataDir = proplists:get_value(data_dir,Config), Mhz=get_cpu_speed(os:type(),DataDir), L = ?MODULE:macro(?MODULE:micros(),DataDir), {Total, Stones} = sum_micros(L, 0, 0), pp(Mhz,Total,Stones,L), {comment,Mhz ++ " MHz, " ++ integer_to_list(Stones) ++ " ESTONES"}. estone_bench(Config) -> DataDir = proplists:get_value(data_dir,Config), L = ?MODULE:macro(?MODULE:micros(),DataDir), {Total, Stones} = sum_micros(L, 0, 0), notify([[{title,"ESTONES"}, {estones, Stones}] | L]), L. -ifndef(PGO). notify(Marks) -> [ct_event:notify( #event{name = benchmark_data, data = [{name,proplists:get_value(title, Mark)}, {value,proplists:get_value(estones, Mark)}]}) || Mark <- Marks]. -else. notify(_) -> ok. -endif. %% The benchmarks to run in order to guide PGO (profile guided optimisation) pgo() -> %% We run all benchmarks except the port_io as we don't want to %% have to build a custom port. Micros = ?MODULE:micros() -- [micro(port_io)], ?MODULE:macro(Micros,[]). %% %% Calculate CPU speed %% %% get_cpu_speed() now returns a string. For multiprocessor %% machines (at least on Solaris) the format is: +[+...] %% get_cpu_speed({win32, _},_DataDir) -> RegH = case catch win32reg:open([read]) of {ok, Handle} -> Handle; _ -> io:format("Error.~nCannot determine CPU clock" "frequency.~n" "Please set the environment variable" "\"CPU_SPEED\"~n"), exit(self(), {error, no_cpu_speed}) end, case win32reg:change_key(RegH,"\\hkey_local_machine\\hardware\\" "description\\system\\centralprocessor" "\\0") of ok -> ok; _ -> io:format("Error.~nRegistry seems to be damaged or" "unavailable.~n" "Please set the environment variable" "\"CPU_SPEED\",~nor correct your registry" "if possible.~n"), win32reg:close(RegH), exit(self(), {error, no_cpu_speed}) end, case win32reg:value(RegH, "~MHZ") of {ok, Speed} -> win32reg:close(RegH), integer_to_list(Speed); _ -> io:format("Error.~nRegistry seems to be damaged or " "unavailable.~n"), io:format("Please set the environment variable" "\"CPU_SPEED\"~n"), win32reg:close(RegH), exit(self(), {error, no_cpu_speed}) end; get_cpu_speed({unix, sunos},DataDir) -> os:cmd(filename:join(DataDir,"sunspeed.sh")) -- "\n"; get_cpu_speed(_Other,_DataDir) -> %% Cannot determine CPU speed "UNKNOWN". %% %% Pretty Print EStone Result %% pp(Mhz,Total,Stones,Ms) -> io:format("EStone test completed~n",[]), io:format("**** CPU speed ~s MHz ****~n",[Mhz]), io:format("**** Total time ~w seconds ****~n", [Total / 1000000]), io:format("**** ESTONES = ~w ****~n~n", [Stones]), io:format("~-31s ~-12s ~-10s % ~-10s ~n~n", [" Title", "Millis", "Estone", "Loops"]), erlang:display({'ESTONES', Stones}), pp2(Ms). sum_micros([], Tot, Stones) -> {Tot, Stones}; sum_micros([H|T], Tot, Sto) -> sum_micros(T, ks(microsecs, H) + Tot, ks(estones, H) + Sto). pp2([]) -> ok; pp2([R|Tail]) -> io:format("~-35s ~-12w ~-10w ~-2w ~-10w ~n", [ks(title,R), round(ks(microsecs, R) / 1000), ks(estones, R), ks(weight_percentage, R), ks(loops, R)]), pp2(Tail). ks(K, L) -> {value, {_, V}} = lists:keysearch(K, 1, L), V. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% EStone test micro(lists) -> #micro{function = lists, weight = 7, loops = 6400, str = "list manipulation"}; micro(msgp) -> #micro{function = msgp, weight = 10, loops = 1515, str = "small messages"}; micro(msgp_medium) -> #micro{function = msgp_medium, weight = 14, loops = 1527, str = "medium messages"}; micro(msgp_huge) -> #micro{function = msgp_huge, weight = 4, loops = 52, str = "huge messages"}; micro(pattern) -> #micro{function = pattern, weight = 5, loops = 1046, str = "pattern matching"}; micro(trav) -> #micro{function = trav, weight = 4, loops = 2834, str = "traverse"}; micro(port_io) -> #micro{function = port_io, weight = 12, loops = 4800, str = "Port i/o"}; micro(large_dataset_work) -> #micro{function = large_dataset_work, weight = 3, loops = 1193, str = "Work with large dataset"}; micro(large_local_dataset_work) -> #micro{function = large_local_dataset_work, weight = 3, loops = 1174, str = "Work with large local dataset"}; micro(alloc) -> #micro{function = alloc, weight = 2, loops = 3710, str = "Alloc and dealloc"}; micro(bif_dispatch) -> #micro{function = bif_dispatch, weight = 5, loops = 1623, str = "Bif dispatch"}; micro(binary_h) -> #micro{function = binary_h, weight = 4, loops = 581, str = "Binary handling"}; micro(ets) -> #micro{function = ets, weight = 6, loops = 342, str = "ets datadictionary"}; micro(generic) -> #micro{function = generic, weight = 9, loops = 7977, str = "Generic server (with timeout)"}; micro(int_arith) -> #micro{function = int_arith, weight = 3, loops = 4157, str = "Small Integer arithmetics"}; micro(float_arith) -> #micro{function = float_arith, weight = 1, loops = 5526, str = "Float arithmetics"}; micro(fcalls) -> #micro{function = fcalls, weight = 5, loops = 882, str = "Function calls"}; micro(timer) -> #micro{function = timer, weight = 2, loops = 2312, str = "Timers"}; micro(links) -> #micro{function = links, weight = 1, loops = 30, str = "Links"}. %% Return a list of micro's micros() -> [ micro(lists), micro(msgp), micro(msgp_medium), micro(msgp_huge), micro(pattern), micro(trav), micro(port_io), micro(large_dataset_work), micro(large_local_dataset_work), micro(alloc), micro(bif_dispatch), micro(binary_h), micro(ets), micro(generic), micro(int_arith), micro(float_arith), micro(fcalls), micro(timer), micro(links) ]. macro(Ms,DataDir) -> statistics(reductions), statistics(runtime), lists(500), %% fixup cache on first round run_micros(Ms,DataDir). run_micros([],_) -> io:nl(), []; run_micros([H|T],DataDir) -> R = run_micro(H,DataDir), [R| run_micros(T,DataDir)]. run_micro(M,DataDir) -> Pid = spawn(?MODULE, run_micro, [self(),M,DataDir]), Res = receive {Pid, Reply} -> Reply end, {value,{title,Title}} = lists:keysearch(title,1,Reply), {value,{estones,Estones}} = lists:keysearch(estones,1,Reply), erlang:display({Title,Estones}), Res. run_micro(Top, M, DataDir) -> EstoneCat = filename:join(DataDir,"estone_cat"), put(estone_cat,EstoneCat), Top ! {self(), apply_micro(M)}. apply_micro(M) -> {GC0, Words0, _} = statistics(garbage_collection), statistics(reductions), Before = monotonic_time(), Compensate = apply_micro(M#micro.function, M#micro.loops), After = monotonic_time(), {GC1, Words1, _} = statistics(garbage_collection), {_, Reds} = statistics(reductions), Elapsed = subtr(Before, After), MicroSecs = Elapsed - Compensate, [{title, M#micro.str}, {tt1, M#micro.tt1}, {function, M#micro.function}, {weight_percentage, M#micro.weight}, {loops, M#micro.loops}, {microsecs,MicroSecs}, {estones, (M#micro.weight * M#micro.weight * ?STONEFACTOR) div max(1,MicroSecs)}, {gcs, GC1 - GC0}, {kilo_word_reclaimed, (Words1 - Words0) div 1000}, {kilo_reductions, Reds div 1000}, {gc_intensity, gci(max(1,Elapsed), GC1 - GC0, Words1 - Words0)}]. monotonic_time() -> try erlang:monotonic_time() catch error:undef -> erlang:now() end. subtr(Before, After) when is_integer(Before), is_integer(After) -> erlang:convert_time_unit(After-Before, native, microsecond); subtr({_,_,_}=Before, {_,_,_}=After) -> timer:now_diff(After, Before). gci(Micros, Words, Gcs) -> ((256 * Gcs) / Micros) + (Words / Micros). apply_micro(Name, Loops) -> io:format("~w(~w)~n", [Name, Loops]), apply(?MODULE, Name, [Loops]). %%%%%%%%%%%% micro bench manipulating lists. %%%%%%%%%%%%%%%%%%%%%%%%% lists(I) -> L1 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", L2 = "aaaaaaaaaa", lists(I, L1, L2). lists(0, _,_) -> 0; lists(I, L1, L2) -> revt(10, L1), appt(10, L1, L2), lists(I-1, L1, L2). revt(0, _) -> done; revt(I, L) -> reverse(L), revt(I-1, L). reverse(L) -> reverse(L, []). reverse([H|T], Ack) -> reverse(T, [H|Ack]); reverse([], Ack) -> Ack. append([H|T], L) -> [H | append(T, L)]; append([], L) -> L. appt(0, _L1, _L2) -> ok; appt(I, L1, L2) -> append(L1, L2), appt(I-1, L1, L2). %%%%%%%%%%%%%%% small message passing and ctxt switching %%%%%%% msgp(I) -> msgp(I, small()). msgp(0, _) -> 0; msgp(I, Msg) -> P1 = spawn(?MODULE, p1, [self()]), P2 = spawn(?MODULE, p1, [P1]), P3 = spawn(?MODULE, p1, [P2]), P4 = spawn(?MODULE, p1, [P3]), msgp_loop(100, P4, Msg), msgp(I-1, Msg). p1(To) -> receive {_From, {message, X}} -> To ! {self(), {message, X}}, p1(To); stop -> To ! stop, exit(normal) end. msgp_loop(0, P, _) -> P ! stop, receive stop -> ok end; msgp_loop(I, P, Msg) -> P ! {self(), {message, Msg}}, receive {_From, {message, _}} -> msgp_loop(I-1, P, Msg) end. %%%%%%%%%%%% large massage passing and ctxt switching %%%%%%% msgp_medium(I) -> msgp_medium(I, big()). msgp_medium(0, _) -> 0; msgp_medium(I, Msg) -> P1 = spawn(?MODULE , p1, [self()]), P2 = spawn(?MODULE, p1, [P1]), P3 = spawn(?MODULE, p1, [P2]), P4 = spawn(?MODULE, p1, [P3]), msgp_loop(100, P4, Msg), msgp_medium(I-1, Msg). %%%%%%%%%%%% huge massage passing and ctxt switching %%%%%%% msgp_huge(I) -> msgp_huge(I, very_big(15)). msgp_huge(0, _) -> 0; msgp_huge(I, Msg) -> P1 = spawn(?MODULE , p1, [self()]), P4 = spawn(?MODULE, p1, [P1]), msgp_loop(100, P4, Msg), msgp_huge(I-1, Msg). %%%%%% typical protocol pattern matching %%%%%%% pattern(0) -> 0; pattern(I) -> Tail = "aaabbaaababba", P1 = [0, 1,2,3,4,5|Tail], pat_loop1(100, P1), pat_loop2(100, P1), pat_loop3(100, P1), pat_loop4(100, P1), pat_loop5(100, P1), pattern(I-1). pat_loop1(0, _) -> ok; pat_loop1(_I, [_, _X, _Y, 0 |_T]) -> ok; pat_loop1(_I, [_, _X, _Y, 1| _T]) -> ok; pat_loop1(_I, [_, _X, _Y, 2 | _T]) -> ok; pat_loop1(I, [_, X, Y, 3 | T]) -> pat_loop1(I-1, [0, X,Y,3|T]). pat_loop2(0, _) -> ok; pat_loop2(_I, [_X, Y | _Tail]) when Y bsl 1 == 0 -> ok; pat_loop2(_I, [_X, Y | _Tail]) when Y bsl 2 == 0 -> ok; pat_loop2(I, [X, Y | Tail]) when Y bsl 2 == 4 -> pat_loop2(I-1, [X, Y |Tail]). pat_loop3(0, _) -> ok; pat_loop3(_I, [{c, h} | _Tail]) -> ok; pat_loop3(_I, [1, 0 |_T]) -> ok; pat_loop3(_I, [X, _Y |_Tail]) when is_binary(X), size(X) == 1 -> ok; pat_loop3(_I, [no, _Y|_Tail]) -> ok; pat_loop3(_I, []) -> ok; pat_loop3(_I, [X,_Y|_T]) when X /= 0 -> ok; pat_loop3(_I, [2,3|_T]) -> ok; pat_loop3(_I, [1, 2]) -> ok; pat_loop3(I, [0, 1 |T]) -> pat_loop3(I-1, [0,1|T]). pat_loop4(0, _) -> ok; pat_loop4(_I, [20|_T]) -> ok; pat_loop4(_I, [219|_T]) -> ok; pat_loop4(_I, [18|_T]) -> ok; pat_loop4(_I, [17|_T]) -> ok; pat_loop4(_I, [16|_T]) -> ok; pat_loop4(_I, [15|_T]) -> ok; pat_loop4(_I, [14|_T]) -> ok; pat_loop4(_I, [13|_T]) -> ok; pat_loop4(_I, [12|_T]) -> ok; pat_loop4(_I, [11|_T]) -> ok; pat_loop4(_I, [10|_T]) -> ok; pat_loop4(_I, [9|_T]) -> ok; pat_loop4(_I, [8|_T]) -> ok; pat_loop4(_I, [7|_T]) -> ok; pat_loop4(_I, [6|_T]) -> ok; pat_loop4(_I, [5|_T]) -> ok; pat_loop4(_I, [4|_T]) -> ok; pat_loop4(_I, [3|_T]) -> ok; pat_loop4(_I, [1|_T]) -> ok; pat_loop4(_I, [21|_T]) -> ok; pat_loop4(_I, [22|_T]) -> ok; pat_loop4(_I, [23|_T]) -> ok; pat_loop4(_I, [24|_T]) -> ok; pat_loop4(_I, [25|_T]) -> ok; pat_loop4(_I, [26|_T]) -> ok; pat_loop4(_I, [27|_T]) -> ok; pat_loop4(I, [0|T]) -> pat_loop4(I-1, [0|T]). pat_loop5(0, _) -> ok; pat_loop5(_I, [0, 20|_T]) -> ok; pat_loop5(_I, [0, 19|_T]) -> ok; pat_loop5(_I, [0, 18|_T]) -> ok; pat_loop5(_I, [0, 17|_T]) -> ok; pat_loop5(_I, [0, 16|_T]) -> ok; pat_loop5(_I, [0, 15|_T]) -> ok; pat_loop5(_I, [0, 14|_T]) -> ok; pat_loop5(_I, [0, 13|_T]) -> ok; pat_loop5(_I, [0, 12|_T]) -> ok; pat_loop5(_I, [0, 11|_T]) -> ok; pat_loop5(_I, [0, 10|_T]) -> ok; pat_loop5(_I, [0, 9|_T]) -> ok; pat_loop5(_I, [0, 8|_T]) -> ok; pat_loop5(_I, [0, 7|_T]) -> ok; pat_loop5(_I, [0, 6|_T]) -> ok; pat_loop5(I, [0, 1|T]) -> pat_loop5(I-1, [0,1|T]). %%%%%%%%%% term traversal representing simple pattern matchhing %%% %%%%%%%%% + some arith trav(I) -> X = very_big(10), trav(I, X). trav(0, _) -> 0; trav(I, T) -> do_trav(T), trav(I-1, T). do_trav(T) when is_tuple(T) -> tup_trav(T, 1, 1 + size(T)); do_trav([H|T]) -> do_trav(H) + do_trav(T); do_trav(X) when is_integer(X) -> 1; do_trav(_X) -> 0. tup_trav(_T, P, P) -> 0; tup_trav(T, P, End) -> do_trav(element(P, T)) + tup_trav(T, P+1, End). %% Port I/O port_io(I) -> EstoneCat = get(estone_cat), Before = monotonic_time(), Pps = make_port_pids(5, I, EstoneCat), %% 5 ports send_procs(Pps, go), After = monotonic_time(), wait_for_pids(Pps), subtr(Before, After). make_port_pids(0, _, _) -> []; make_port_pids(NoPorts, J, EstoneCat) -> [spawn(?MODULE, ppp, [self(),J,EstoneCat]) | make_port_pids(NoPorts-1, J, EstoneCat)]. ppp(Top, I, EstoneCat) -> P = open_port({spawn, EstoneCat}, []),%% cat sits at the other end Str = lists:duplicate(200, 88), %% 200 X'es Cmd = {self(), {command, Str}}, receive go -> ok end, ppp_loop(P, I, Cmd), Cmd2 = {self(), {command, "abcde"}}, Res = ppp_loop(P, I, Cmd2), P ! {self(), close}, receive {P, closed} -> closed end, Top ! {self(), Res}. ppp_loop(_P, 0, _) -> ok; ppp_loop(P, I, Cmd) -> P ! Cmd, receive {P, _} -> %% no match ppp_loop(P, I-1, Cmd) end. %% Working with a very large non-working data set %% where the passive data resides in remote processes large_dataset_work(I) -> {Minus, Ps} = timer:tc(?MODULE, mk_big_procs, [?BIGPROCS]), trav(I), lists(I), send_procs(Ps, stop), Minus. %% Don't count time to create the big procs. mk_big_procs(0) -> []; mk_big_procs(I) -> [ mk_big_proc()| mk_big_procs(I-1)]. mk_big_proc() -> P = spawn(?MODULE, big_proc, []), P ! {self(), running}, receive {P, yes} -> P end. big_proc() -> X = very_big(?BIGPROC_SIZE), %% creates a big heap Y = very_big(?BIGPROC_SIZE), Z = very_big(?BIGPROC_SIZE), receive {From, running} -> From ! {self(), yes} end, receive stop -> {X, Y, Z} %% Can't be garbed away now by very (not super) %% smart compiler end. %% Working with a large non-working data set %% where the data resides in the local process. large_local_dataset_work(I) -> {Minus, _Data} = timer:tc(?MODULE, very_big, [?BIGPROC_SIZE]), trav(I), lists(I), Minus. %% Fast allocation and also deallocation that is gc test %% Important to not let variable linger on the stack un-necessarily alloc(0) -> 0; alloc(I) -> _X11 = very_big(), _X12 = very_big(), _X13 = very_big(), _Z = [_X14 = very_big(), _X15 = very_big(), _X16 = very_big()], _X17 = very_big(), _X18 = very_big(), _X19 = very_big(), _X20 = very_big(), _X21 = very_big(), _X22 = very_big(), _X23 = very_big(), _X24 = very_big(), alloc(I-1). %% Time to call bif's %% Lot's of element stuff which reflects the record code which %% is becoming more and more common bif_dispatch(0) -> 0; bif_dispatch(I) -> disp(), disp(), disp(), disp(), disp(), disp(), disp(), disp(), disp(), disp(), disp(), disp(), bif_dispatch(I-1). disp() -> Tup = {a}, L = [x], self(),self(),self(),self(),self(),self(),self(),self(),self(), make_ref(), atom_to_list(''), _X = list_to_atom([]), tuple_to_list({}), _X2 = list_to_tuple([]), element(1, Tup), element(1, Tup), _Elem = element(1, Tup),element(1, Tup),element(1, Tup),element(1, Tup), element(1, Tup),element(1, Tup),element(1, Tup),element(1, Tup), element(1, Tup),element(1, Tup),element(1, Tup),element(1, Tup), element(1, Tup),element(1, Tup),element(1, Tup),element(1, Tup), setelement(1, Tup,k), setelement(1, Tup,k), setelement(1, Tup,k),setelement(1, Tup,k),setelement(1, Tup,k), setelement(1, Tup,k),setelement(1, Tup,k),setelement(1, Tup,k), setelement(1, Tup,k), setelement(1, Tup,k), setelement(1, Tup,k), setelement(1, Tup,k), _Y = setelement(1, Tup,k), _Date = date(), time(), put(a, 1), get(a), erase(a), hd(L), tl(L), _Len = length(L),length(L),length(L),length(L), node(),node(),node(),node(),node(),node(),node(),node(), S=self(), node(S),node(S),node(S), size(Tup), _W = whereis(code_server),whereis(code_server), whereis(code_server),whereis(code_server), whereis(code_server),whereis(code_server), _W2 = whereis(code_server). %% Generic server like behaviour generic(I) -> register(funky, spawn(?MODULE, gserv, [funky, ?MODULE, [], []])), g_loop(I). g_loop(0) -> exit(whereis(funky), kill), 0; g_loop(I) -> ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [xyz]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [xyz]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), ?MODULE:req(funky, {call, [abc]}), g_loop(I-1). req(Name, Req) -> R = make_ref(), Name ! {self(), R, Req}, receive {Name, R, Reply} -> Reply after 2000 -> exit(timeout) end. gserv(Name, Mod, State, Debug) -> receive {From, Ref, {call, Req}} when Debug == [] -> case catch apply(Mod, handle_call, [From, State, Req]) of {reply, Reply, State2} -> From ! {Name, Ref, Reply}, gserv(Name, Mod, State2, Debug); {noreply, State2} -> gserv(Name, Mod, State2, Debug); {'EXIT', Reason} -> exit(Reason) end; {_From, _Ref, _Req} when Debug /= [] -> exit(nodebug) end. handle_call(_From, _State, [xyz]) -> R = atom_to_list(xyz), {reply, R, []}; handle_call(_From, State, [abc]) -> R = 1 + 3, {reply, R, [R | State]}. %% Binary handling, creating, manipulating and sending binaries binary_h(I) -> Before = monotonic_time(), P = spawn(?MODULE, echo, [self()]), B = list_to_binary(lists:duplicate(2000, 5)), After = monotonic_time(), Compensate = subtr(Before, After), binary_h_2(I, P, B), Compensate. binary_h_2(0, P, _B) -> exit(P, kill); binary_h_2(I, P, B) -> echo_loop(P, 20, B), split_loop(B, {abc,1,2222,self(),"ancnd"}, 100), binary_h_2(I-1, P, B). split_loop(_B, _, 0) -> ok; split_loop(B, Term, I) -> {X, Y} = split_binary(B, I), size(X), binary_to_list(Y, 1, 2), binary_to_term(term_to_binary(Term)), split_loop(B, Term, I-1). echo_loop(_P, 0, _B) -> k; echo_loop(P, I, B) -> P ! B, P ! B, P ! B, P ! B, P ! B, P ! B, P ! B, P ! B, P ! B, P ! B, receive _ -> ok end, receive _ -> ok end, receive _ -> ok end, receive _ -> ok end, receive _ -> ok end, receive _ -> ok end, receive _ -> ok end, receive _ -> ok end, receive _ -> ok end, receive _ -> ok end, echo_loop(P, I-1, B). ets(0) -> 0; ets(I) -> T1 = ets:new(a, [set]), T2 = ets:new(c, [bag, private]), L = [T1, T2], run_tabs(L, L, 1), ets:delete(T1), ets:delete(T2), ets(I-1). run_tabs(_, _, 0) -> ok; run_tabs([], L, I) -> run_tabs(L, L, I-1); run_tabs([Tab|Tail], L, I) -> Begin = I * 20, End = (I+1) * 20, run_tab(Tab, Begin, End, I), run_tabs(Tail, L, I). run_tab(_Tab, X, X, _) -> ok; run_tab(Tab, Beg, End, J) -> ets:insert(Tab, {Beg, J}), ets:insert(Tab, {J, Beg}), ets:insert(Tab, {{foo,Beg}, J}), ets:insert(Tab, {{foo, J}, Beg}), ets:delete(Tab, haha), ets:match_delete(Tab, {k, j}), ets:match(Tab, {Beg, '$1'}), ets:match(Tab, {'$1', J}), ets:delete(Tab, Beg), K = ets:first(Tab), _K2 = ets:next(Tab, K), run_tab(Tab, Beg+1, End, J). %%%% Integer arith %%%%% int_arith(0) -> 0; int_arith(I) -> do_arith(I) + do_arith(I) + do_arith(I) + do_arith(I) + do_arith(I) + do_arith(I) + do_arith(I) + do_arith(I) + do_arith(I) + 66, int_arith(I-1). do_arith(I) -> do_arith2(I) - do_arith2(I) - do_arith2(I) - do_arith2(I) - do_arith2(I) - do_arith2(I) - do_arith2(I) - 99. do_arith2(I) -> X = 23, _Y = 789 + I, Z = I + 1, U = (X bsl 1 bsr I) * X div 2 bsr 4, U1 = Z + Z + Z + Z + X bsl 4 * 2 bsl 2, Z - U + U1 div 2. %%%% Float arith %%%%% float_arith(0) -> 0; float_arith(I) -> f_do_arith(I) + f_do_arith(I) + f_do_arith(I) + f_do_arith(I) + f_do_arith(I) + f_do_arith(I) + f_do_arith(I) + f_do_arith(I) + f_do_arith(I) + 66, float_arith(I-1). f_do_arith(I) -> X = 23.4, _Y = 789.99 + I, Z = I + 1.88, U = (X * 1 / I) * X / 2 * 4, U1 = Z + Z + Z + Z + X * 4 * 2 / 2, Z - U + U1 / 2. %%%% time to do various function calls fcalls(0) -> 0; fcalls(I) -> local0(400), remote0(400), app0(400), local1(400), remote1(400), app1(400), fcalls(I-1). local0(0) -> 0; local0(N) -> local0(N-1). local1(0) -> 0; local1(N) -> 1+local1(N-1). remote0(0) -> 0; remote0(N) -> ?MODULE:remote0(N-1). remote1(0) -> 0; remote1(N) -> 1+?MODULE:remote1(N-1). app0(0) -> 0; app0(N) -> apply(?MODULE, app0, [N-1]). app1(0) -> 0; app1(N) -> 1 + apply(?MODULE, app1, [N-1]). %%%%%% jog the time queue implementation timer(I) -> L = [50, 50, 50, 100, 1000, 3000, 8000, 50000, 100000], timer(I, L). timer(0, _) -> 0; timer(N, L) -> send_self(100), recv(100,L, L), timer(N-1). recv(0, _, _) -> ok; recv(N, [], L) -> recv(N, L, L); recv(N, [Timeout|Tail], L) -> receive hi_dude -> recv(N-1, Tail, L) after Timeout -> io:format("XXXXX this wasn't supposed to happen???~n", []), ok end. send_self(0) -> ok; send_self(N) -> self() ! hi_dude, send_self(N-1). %%%%%%%%%%%% managing many links %%%%% links(I) -> L = mk_link_procs(100), send_procs(L, {procs, L, I}), wait_for_pids(L), 0. mk_link_procs(0) -> []; mk_link_procs(I) -> [spawn_link(?MODULE, lproc, [self()]) | mk_link_procs(I-1)]. lproc(Top) -> process_flag(trap_exit,true), receive {procs, Procs, I} -> Top ! {self(), lproc(Procs, Procs, link, I)} end. lproc(_, _, _, 0) -> done; lproc([], Procs, link, I) -> lproc(Procs, Procs, unlink, I-1); lproc([], Procs, unlink, I) -> lproc(Procs, Procs, link, I-1); lproc([Pid|Tail], Procs, unlink, I) -> unlink(Pid), lproc(Tail, Procs, unlink, I); lproc([Pid|Tail], Procs, link, I) -> link(Pid), lproc(Tail, Procs, unlink, I). %%%%%%%%%%% various utility functions %%%%%%% echo(Pid) -> receive X -> Pid ! X, echo(Pid) end. very_big() -> very_big(2). very_big(0) -> []; very_big(I) -> {1,2,3,a,v,f,r,t,y,u,self(), self(), self(), "22222222222222222", {{"234", self()}}, [[very_big(I-1)]]}. big() -> {self(), funky_stuff, baby, {1, [123, true,[]], "abcdef"}}. small() -> {self(), true}. %% Wait for a list of children to respond wait_for_pids([]) -> ok; wait_for_pids([P|Tail]) -> receive {P, _Res} -> wait_for_pids(Tail) end. send_procs([P|Tail], Msg) -> P ! Msg, send_procs(Tail, Msg); send_procs([], _) -> ok.