%% ========================================================================================================== %% Syn - A global Process Registry and Process Group manager. %% %% The MIT License (MIT) %% %% Copyright (c) 2019 Roberto Ostinelli and Neato Robotics, Inc. %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the "Software"), to deal %% in the Software without restriction, including without limitation the rights %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell %% copies of the Software, and to permit persons to whom the Software is %% furnished to do so, subject to the following conditions: %% %% The above copyright notice and this permission notice shall be included in %% all copies or substantial portions of the Software. %% %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN %% THE SOFTWARE. %% ========================================================================================================== -module(syn_benchmark). %% API -export([start/0]). -export([process_loop/0]). -export([collection_loop/2]). -export([run_test_on_node/3]). -export([register_pids/1]). -export([unregister_pids/1]). -export([wait_for_unregistered/1]). %% macros -define(MAX_RETRIEVE_WAITING_TIME, 60000). %% =================================================================== %% API %% =================================================================== start() -> %% init NodeCount = list_to_integer(os:getenv("SYN_BENCH_NODE_COUNT", "2")), ProcessCount = list_to_integer(os:getenv("SYN_PROCESS_COUNT", "100000")), %% start nodes lists:foreach(fun(Count) -> ShortName = list_to_atom("syn_bench_slave_" ++ integer_to_list(Count)), {ok, _} = syn_test_suite_helper:start_slave(ShortName) end, lists:seq(1, NodeCount - 1)), Nodes = [node() | nodes()], io:format("-----> Started ~p nodes: ~p~n", [length(Nodes), Nodes]), %% start syn everywhere lists:foreach(fun(Node) -> ok = rpc:call(Node, syn, start, []) end, Nodes), timer:sleep(1000), %% compute names per node ProcessesPerNode = round(ProcessCount / length(Nodes)), io:format("-----> ~p processes per node~n", [ProcessesPerNode]), %% launch test everywhere CollectingPid = self(), lists:foldl(fun(Node, Acc) -> FromName = Acc * ProcessesPerNode + 1, ToName = FromName + ProcessesPerNode - 1, rpc:cast(Node, ?MODULE, run_test_on_node, [CollectingPid, FromName, ToName]), Acc + 1 end, 0, Nodes), Results = collection_loop(Nodes, []), io:format("-----> Results: ~p~n", [Results]), %% stop syn everywhere lists:foreach(fun(Node) -> ok = rpc:call(Node, syn, stop, []) end, [node() | nodes()]), timer:sleep(1000), %% stop nodes lists:foreach(fun(SlaveNode) -> syn_test_suite_helper:connect_node(SlaveNode), ShortName = list_to_atom(lists:nth(1, string:split(atom_to_list(SlaveNode), "@"))), syn_test_suite_helper:stop_slave(ShortName) end, nodes()), io:format("-----> Stopped ~p nodes: ~p~n", [length(Nodes), Nodes]), %% stop node init:stop(). run_test_on_node(CollectingPid, FromName, ToName) -> %% launch processes - list is in format {Name, pid()} PidTuples = [{Name, spawn(?MODULE, process_loop, [])} || Name <- lists:seq(FromName, ToName)], {ToName, ToPid} = lists:last(PidTuples), %% register {TimeReg0, _} = timer:tc(?MODULE, register_pids, [PidTuples]), RegisteringRate1 = length(PidTuples) / TimeReg0 * 1000000, %% check ToPid = syn:whereis(ToName), %% unregister {TimeReg1, _} = timer:tc(?MODULE, unregister_pids, [PidTuples]), UnRegisteringRate1 = length(PidTuples) / TimeReg1 * 1000000, %% check undefined = syn:whereis(ToName), %% re-register {TimeReg3, _} = timer:tc(?MODULE, register_pids, [PidTuples]), RegisteringRate2 = length(PidTuples) / TimeReg3 * 1000000, %% check ToPid = syn:whereis(ToName), %% kill all lists:foreach(fun({_Name, Pid}) -> exit(Pid, kill) end, PidTuples), %% check all unregistered {TimeReg4, _} = timer:tc(?MODULE, wait_for_unregistered, [ToName]), CheckAllKilled = TimeReg4 / 1000000, %% return CollectingPid ! {node(), [ {registering_rate_1, RegisteringRate1}, {unregistering_rate_1, UnRegisteringRate1}, {registering_rate_2, RegisteringRate2}, {check_all_killed, CheckAllKilled} ]}. %% =================================================================== %% Internal %% =================================================================== collection_loop([], Results) -> Results; collection_loop(Nodes, Results) -> receive {Node, Result} -> collection_loop(lists:delete(Node, Nodes), [{Node, Result} | Results]) after ?MAX_RETRIEVE_WAITING_TIME -> io:format("COULD NOT COMPLETE TEST IN ~p seconds", [?MAX_RETRIEVE_WAITING_TIME]) end. process_loop() -> receive _ -> ok end. register_pids([]) -> ok; register_pids([{Name, Pid} | TPidTuples]) -> ok = syn:register(Name, Pid), register_pids(TPidTuples). wait_for_unregistered(ToName) -> case syn:whereis(ToName) of undefined -> ok; _ -> timer:sleep(50), wait_for_unregistered(ToName) end. unregister_pids([]) -> ok; unregister_pids([{Name, _Pid} | TPidTuples]) -> ok = syn:unregister(Name), unregister_pids(TPidTuples).