SPSBenchmark/src/requester.erl

154 lines
5.0 KiB
Erlang

-module(requester).
-export([init/0, startServiceTime/1, startOpenLoop/2, startClosedLoop/3,
waiting/1, stop/1, printActiveWorkers/1, exponential_time/1]).
%% @spec init() -> pid()
%% @doc Initialize the requester.
%% This fuction will not return until all the pages will be read from
%% the file and shuffled.
init() ->
Filename = application:get_env(spsBenchmark, pageFile, "pages.csv"),
Domain = application:get_env(spsBenchmark, domain, "https://en.wikipedia.org/wiki/"),
NumPages = application:get_env(spsBenchmark, nPages, 1000),
Pages = shuffle:list(resources:n_pages(Filename, 0, NumPages)),
Links = resources:urls(Domain, Pages),
Pid = spawn(?MODULE, waiting, [Links]),
Pid.
%% @spec waiting(Links::[string()]) -> void()
%% @doc Keep the links while waiting to start the requester.
waiting(Links) ->
receive
{service_time} -> service_time(Links);
{open_loop, Rate} -> open_loop(Rate, Links, 0);
{closed_loop, Rate, NumWorkers} -> closed_loop(Rate, NumWorkers, Links)
end.
%% @spec startServiceTime(Pid::pid()) -> void()
%% @doc Start the requests to calculate the service time.
startServiceTime(Pid) ->
Pid ! {service_time}.
%% @spec startOpenLoop(Pid::pid(), Rate::float()) -> void()
%% @doc Start the open loop requests.
startOpenLoop(Pid, Rate) ->
Pid ! {open_loop, Rate}.
%% @spec startClosedLoop(Pid::pid(), Rate::float(),
%% NumWorkers::integer()) -> void()
%% @doc Start the closed loop requests.
startClosedLoop(Pid, Rate, NumWorkers) ->
Pid ! {closed_loop, Rate, NumWorkers}.
%% @spec stop(Pid::pid()) -> void()
%% @doc Stop the benchmark.
stop(Pid) ->
Pid ! {stop}.
%% @spec printActiveWorkers(Pid::pid()) -> void()
%% @doc Print the number of active workers of the requester with the
%% specific pid.
printActiveWorkers(Pid) ->
Pid ! {print_active_workers}.
%% @spec service_time(Links::[string()]) -> void()
%% @doc Generate a single request to calculate the service time and then wait.
service_time(Links) ->
case Links of
[] -> ok;
[Link | RemainigLinks] ->
generate_worker(Link),
service_time_wait(RemainigLinks)
end.
%% @spec service_time_wait(Links::[string()]) -> void()
%% @doc Wait until the service time request ends and then generate a new one.
service_time_wait(Links) ->
receive
{'DOWN', _MonitorReference, process, _Pid, _Reason} ->
service_time(Links);
{print_active_workers} ->
service_time_wait(Links);
{stop} -> ok
end.
%% @spec open_loop(Rate::float(), Links::[string()],
%% ActiveWorkers::integer()) -> ok
%% @doc Generate requests in open loop with an exponential sampling.
open_loop(Rate, Links, ActiveWorkers) ->
receive
{'DOWN', _MonitorReference, process, _Pid, _Reason} ->
open_loop(Rate, Links, ActiveWorkers - 1);
{print_active_workers} ->
io:format("Num active requests: ~p~n", [ActiveWorkers]),
open_loop(Rate, Links, ActiveWorkers);
{stop} -> ok
after exponential_time(Rate) ->
case Links of
[] -> case ActiveWorkers of
0 -> ok;
_ -> open_loop(Rate, Links, ActiveWorkers)
end;
[Link | RemainigLinks] ->
generate_worker(Link),
open_loop(Rate, RemainigLinks, ActiveWorkers + 1)
end
end.
%% @spec closed_loop(Rate::float(), NumWorkers::integer(),
%% Links::[string()]) -> ok
%% @doc Generate requests in closed loop.
closed_loop(Rate, NumWorkers, Links) ->
closed_loop_aux(Rate, NumWorkers,
partition:list(Links, length(Links) div NumWorkers)).
%% @spec closed_loop_aux(Rate::float(), NumWorkers::integer(),
%% LinksLists::[[string()]]) -> ok
%% @doc Auxiliary function for closed_loop that takes the Links already
%% partitioned for each worker.
closed_loop_aux(Rate, NumWorkers, LinksLists) ->
case NumWorkers of
0 -> closed_loop_wait();
_ ->
[WorkersLinks | OtherLinks] = LinksLists,
generate_loop_worker(WorkersLinks, Rate),
closed_loop_aux(Rate, NumWorkers - 1, OtherLinks)
end.
%% @spec closed_loop_wait() -> void()
%% @doc Wait until the closed loop is stopped.
closed_loop_wait() ->
receive
{stop} -> exit(shutdown)
end.
%% @spec exponential_time(Rate::float()) -> integer()
%% @doc Get the number of millisecond to wait sampling
%% an exponential variable with specific rate.
exponential_time(Rate) ->
round(exponential(Rate) * 1000).
%% @spec exponential(Rate::float()) -> float()
%% @doc Get the sample of an exponential variable with specific rate.
exponential(Rate) ->
-math:log(rand:uniform()) / Rate.
%% @spec generate_worker(Url::string()) -> void
%% @doc Spawn and monitor a worker for a given url.
generate_worker(Url) ->
spawn_monitor(
requestWorker,
request,
[Url]
).
%% @spec generate_loop_worker(Urls::[string()], Rate::float()) -> void
%% @doc Spawn and link to a worker that makes requests in a loop
%% using the given urls.
generate_loop_worker(Urls, Rate) ->
spawn_link(
requestWorker,
loop_request,
[Urls, Rate]
).