123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- -module(bench_take_return).
- -export([
- size_5_take_return_one/1,
- bench_size_5_take_return_one/2,
- size_1000_take_return_one/1,
- bench_size_1000_take_return_one/2,
- size_1_empty_take_no_members/1,
- bench_size_1_empty_take_no_members/2,
- size_5_take_return_all/1,
- bench_size_5_take_return_all/2,
- size_500_take_return_all/1,
- bench_size_500_take_return_all/2,
- size_500_clients_50_take_return_all/1,
- bench_size_500_clients_50_take_return_all/2,
- size_0_max_500_take_return_all/1,
- bench_size_0_max_500_take_return_all/2,
- size_0_max_500_clients_50_take_return_all/1,
- bench_size_0_max_500_clients_50_take_return_all/2
- ]).
- %% @doc Pool of fixed size 5 - try to take just one member and instantly return
- size_5_take_return_one(init) ->
- % init is called only once in the same process where benchmark will be running
- % at the very beginning of benchmark.
- % Value returned from this callback will be passed to server({input, State}),
- % bench_server(_, State) and server({stop, State})
- start_fixed(?FUNCTION_NAME, 5),
- ?FUNCTION_NAME;
- size_5_take_return_one({input, _Server}) ->
- % This callback is called after `init` to generate benchmark input data.
- % Returned value will be passed to bench_server(Input, _)
- [];
- size_5_take_return_one({stop, PoolName}) ->
- % Called only once at the very end of benchmark
- stop(PoolName).
- bench_size_5_take_return_one(_Input, PoolName) ->
- Member = pooler:take_member(PoolName),
- true = is_pid(Member),
- pooler:return_member(PoolName, Member).
- %% @doc Pool of fixed size 1000 - try to take just one member and instantly return
- size_1000_take_return_one(init) ->
- start_fixed(?FUNCTION_NAME, 1000),
- ?FUNCTION_NAME;
- size_1000_take_return_one({input, _Server}) ->
- % This callback is called after `init` to generate benchmark input data.
- % Returned value will be passed to bench_server(Input, _)
- [];
- size_1000_take_return_one({stop, PoolName}) ->
- % Called only once at the very end of benchmark
- stop(PoolName).
- bench_size_1000_take_return_one(_Input, PoolName) ->
- Member = pooler:take_member(PoolName),
- true = is_pid(Member),
- pooler:return_member(PoolName, Member).
- %% @doc Fully satrurated pool of fixed size 1 - try to take just one member, get `error_no_members'
- size_1_empty_take_no_members(init) ->
- start_fixed(?FUNCTION_NAME, 1),
- Worker = pooler:take_member(?FUNCTION_NAME),
- {?FUNCTION_NAME, Worker};
- size_1_empty_take_no_members({input, _}) ->
- [];
- size_1_empty_take_no_members({stop, {PoolName, Worker}}) ->
- pooler:return_member(PoolName, Worker),
- stop(PoolName).
- bench_size_1_empty_take_no_members(_Input, {PoolName, _}) ->
- error_no_members = pooler:take_member(PoolName).
- %% @doc Pool of fixed size 5 - take all members sequentially and instantly return them
- size_5_take_return_all(init) ->
- start_fixed(?FUNCTION_NAME, 5),
- {?FUNCTION_NAME, 5};
- size_5_take_return_all({input, {_Pool, _Size}}) ->
- [];
- size_5_take_return_all({stop, {PoolName, _}}) ->
- stop(PoolName).
- bench_size_5_take_return_all(_Input, {PoolName, Size}) ->
- Members = take_n(PoolName, Size),
- [pooler:return_member(PoolName, Member) || Member <- Members].
- %% @doc Pool of fixed size 500 - take all members and instantly return them
- size_500_take_return_all(init) ->
- start_fixed(?FUNCTION_NAME, 500),
- {?FUNCTION_NAME, 500};
- size_500_take_return_all({input, {_Pool, Size}}) ->
- lists:seq(1, Size);
- size_500_take_return_all({stop, {PoolName, _}}) ->
- stop(PoolName).
- bench_size_500_take_return_all(_Input, {PoolName, Size}) ->
- Members = take_n(PoolName, Size),
- [pooler:return_member(PoolName, Member) || Member <- Members].
- %% @doc Pool of fixed size 500 - take all members from 50 workers and let workers instantly return them
- size_500_clients_50_take_return_all(init) ->
- PoolSize = 500,
- NumClients = 50,
- PerClient = PoolSize div NumClients,
- start_fixed(?FUNCTION_NAME, 500),
- Clients = [
- erlang:spawn_link(
- fun() ->
- client(?FUNCTION_NAME, 0, PerClient)
- end
- )
- || _ <- lists:seq(1, NumClients)
- ],
- {?FUNCTION_NAME, 500, Clients};
- size_500_clients_50_take_return_all({input, {_Pool, _Size, _Clients}}) ->
- [];
- size_500_clients_50_take_return_all({stop, {PoolName, _Size, Clients}}) ->
- [
- begin
- unlink(Pid),
- exit(Pid, shutdown)
- end
- || Pid <- Clients
- ],
- stop(PoolName).
- bench_size_500_clients_50_take_return_all(_Input, {_PoolName, _Size, Clients}) ->
- Ref = erlang:make_ref(),
- Self = self(),
- lists:foreach(fun(C) -> C ! {do, Self, Ref} end, Clients),
- lists:foreach(
- fun(_C) ->
- receive
- {done, RecRef} ->
- RecRef = Ref
- after 5000 ->
- error(timeout)
- end
- end,
- Clients
- ).
- %% @doc Artificial example: pool with init_count=0, max_count=500 that is culled to 0 on each iteration.
- %% Try to take 500 workers sequentially and instantly return them (and trigger culling).
- %% This benchmark, while is quite unrealistic (why have pool that instantly kills workers), but it
- %% helps to test worker spawn/kill routines.
- size_0_max_500_take_return_all(init) ->
- Size = 500,
- start([
- {name, ?FUNCTION_NAME},
- {init_count, 0},
- {max_count, Size},
- {max_age, {0, sec}}
- ]),
- {?FUNCTION_NAME, Size};
- size_0_max_500_take_return_all({input, {_Pool, _Size}}) ->
- [];
- size_0_max_500_take_return_all({stop, {PoolName, _}}) ->
- stop(PoolName).
- bench_size_0_max_500_take_return_all(_Input, {PoolName, Size}) ->
- Members = take_n(PoolName, 500, Size),
- [pooler:return_member(PoolName, Member) || Member <- Members],
- whereis(PoolName) ! cull_pool,
- Utilization = pooler:pool_utilization(PoolName),
- 0 = proplists:get_value(free_count, Utilization),
- 0 = proplists:get_value(in_use_count, Utilization).
- %% @doc Pool with init_count=0, max_count=500 that is culled to 0 on each iteration, taken by 50 workers
- %%
- %% Artificial example (because pool is instantly culled), but with parallel consumers we can test concurrent
- %% pool worker starter
- size_0_max_500_clients_50_take_return_all(init) ->
- PoolSize = 500,
- NumClients = 50,
- PerClient = PoolSize div NumClients,
- start([
- {name, ?FUNCTION_NAME},
- {init_count, 0},
- {max_count, PoolSize},
- {max_age, {0, sec}},
- {queue_max, 500}
- ]),
- Clients = [
- erlang:spawn_link(
- fun() ->
- client(?FUNCTION_NAME, 500, PerClient)
- end
- )
- || _ <- lists:seq(1, NumClients)
- ],
- {?FUNCTION_NAME, 500, Clients};
- size_0_max_500_clients_50_take_return_all({input, {_Pool, _Size, _Clients}}) ->
- [];
- size_0_max_500_clients_50_take_return_all({stop, {PoolName, _Size, Clients}}) ->
- [
- begin
- unlink(Pid),
- exit(Pid, shutdown)
- end
- || Pid <- Clients
- ],
- stop(PoolName).
- bench_size_0_max_500_clients_50_take_return_all(_Input, {PoolName, _Size, Clients}) ->
- Ref = erlang:make_ref(),
- Self = self(),
- lists:foreach(fun(C) -> C ! {do, Self, Ref} end, Clients),
- lists:foreach(
- fun(_C) ->
- receive
- {done, RecRef} ->
- RecRef = Ref
- after 5000 ->
- error(timeout)
- end
- end,
- Clients
- ),
- whereis(PoolName) ! cull_pool,
- Utilization = pooler:pool_utilization(PoolName),
- 0 = proplists:get_value(free_count, Utilization),
- 0 = proplists:get_value(in_use_count, Utilization).
- %% Internal
- start_fixed(Name, Size) ->
- Conf = [
- {name, Name},
- {init_count, Size},
- {max_count, Size}
- ],
- start(Conf).
- start(Conf0) ->
- Conf = [{start_mfa, {pooled_gs, start_link, [{"test"}]}} | Conf0],
- logger:set_handler_config(default, filters, []),
- {ok, _} = application:ensure_all_started(pooler),
- {ok, _} = pooler:new_pool(Conf).
- stop(Name) ->
- pooler:rm_pool(Name),
- application:stop(pooler).
- take_n(PoolName, N) ->
- take_n(PoolName, 0, N).
- take_n(_, _, 0) ->
- [];
- take_n(PoolName, Timeout, N) ->
- Member = pooler:take_member(PoolName, Timeout),
- true = is_pid(Member),
- [Member | take_n(PoolName, Timeout, N - 1)].
- client(Pool, Timeout, N) ->
- receive
- {do, From, Ref} ->
- Taken = take_n(Pool, Timeout, N),
- [pooler:return_member(Pool, Member) || Member <- Taken],
- From ! {done, Ref}
- end,
- client(Pool, Timeout, N).
|