bench_take_return.erl 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. -module(bench_take_return).
  2. -export([
  3. size_5_take_return_one/1,
  4. bench_size_5_take_return_one/2,
  5. size_1000_take_return_one/1,
  6. bench_size_1000_take_return_one/2,
  7. size_1_empty_take_no_members/1,
  8. bench_size_1_empty_take_no_members/2,
  9. size_5_take_return_all/1,
  10. bench_size_5_take_return_all/2,
  11. size_500_take_return_all/1,
  12. bench_size_500_take_return_all/2,
  13. size_500_clients_50_take_return_all/1,
  14. bench_size_500_clients_50_take_return_all/2,
  15. size_0_max_500_take_return_all/1,
  16. bench_size_0_max_500_take_return_all/2
  17. ]).
  18. %% @doc Pool of fixed size 5 - try to take just one member and instantly return
  19. size_5_take_return_one(init) ->
  20. % init is called only once in the same process where benchmark will be running
  21. % at the very beginning of benchmark.
  22. % Value returned from this callback will be passed to server({input, State}),
  23. % bench_server(_, State) and server({stop, State})
  24. start_fixed(?FUNCTION_NAME, 5),
  25. ?FUNCTION_NAME;
  26. size_5_take_return_one({input, _Server}) ->
  27. % This callback is called after `init` to generate benchmark input data.
  28. % Returned value will be passed to bench_server(Input, _)
  29. [];
  30. size_5_take_return_one({stop, PoolName}) ->
  31. % Called only once at the very end of benchmark
  32. stop(PoolName).
  33. bench_size_5_take_return_one(_Input, PoolName) ->
  34. Member = pooler:take_member(PoolName),
  35. true = is_pid(Member),
  36. pooler:return_member(PoolName, Member).
  37. %% @doc Pool of fixed size 1000 - try to take just one member and instantly return
  38. size_1000_take_return_one(init) ->
  39. start_fixed(?FUNCTION_NAME, 1000),
  40. ?FUNCTION_NAME;
  41. size_1000_take_return_one({input, _Server}) ->
  42. % This callback is called after `init` to generate benchmark input data.
  43. % Returned value will be passed to bench_server(Input, _)
  44. [];
  45. size_1000_take_return_one({stop, PoolName}) ->
  46. % Called only once at the very end of benchmark
  47. stop(PoolName).
  48. bench_size_1000_take_return_one(_Input, PoolName) ->
  49. Member = pooler:take_member(PoolName),
  50. true = is_pid(Member),
  51. pooler:return_member(PoolName, Member).
  52. %% @doc Fully satrurated pool of fixed size 1 - try to take just one member, get `error_no_members'
  53. size_1_empty_take_no_members(init) ->
  54. start_fixed(?FUNCTION_NAME, 1),
  55. Worker = pooler:take_member(?FUNCTION_NAME),
  56. {?FUNCTION_NAME, Worker};
  57. size_1_empty_take_no_members({input, _}) ->
  58. [];
  59. size_1_empty_take_no_members({stop, {PoolName, Worker}}) ->
  60. pooler:return_member(PoolName, Worker),
  61. stop(PoolName).
  62. bench_size_1_empty_take_no_members(_Input, {PoolName, _}) ->
  63. error_no_members = pooler:take_member(PoolName).
  64. %% @doc Pool of fixed size 5 - take all members sequentially and instantly return them
  65. size_5_take_return_all(init) ->
  66. start_fixed(?FUNCTION_NAME, 5),
  67. {?FUNCTION_NAME, 5};
  68. size_5_take_return_all({input, {_Pool, _Size}}) ->
  69. [];
  70. size_5_take_return_all({stop, {PoolName, _}}) ->
  71. stop(PoolName).
  72. bench_size_5_take_return_all(_Input, {PoolName, Size}) ->
  73. Members = take_n(PoolName, Size),
  74. [pooler:return_member(PoolName, Member) || Member <- Members].
  75. %% @doc Pool of fixed size 500 - take all members and instantly return them
  76. size_500_take_return_all(init) ->
  77. start_fixed(?FUNCTION_NAME, 500),
  78. {?FUNCTION_NAME, 500};
  79. size_500_take_return_all({input, {_Pool, Size}}) ->
  80. lists:seq(1, Size);
  81. size_500_take_return_all({stop, {PoolName, _}}) ->
  82. stop(PoolName).
  83. bench_size_500_take_return_all(_Input, {PoolName, Size}) ->
  84. Members = take_n(PoolName, Size),
  85. [pooler:return_member(PoolName, Member) || Member <- Members].
  86. %% @doc Pool of fixed size 500 - take all members from 50 workers and let workers instantly return them
  87. size_500_clients_50_take_return_all(init) ->
  88. PoolSize = 500,
  89. NumClients = 50,
  90. PerClient = PoolSize div NumClients,
  91. start_fixed(?FUNCTION_NAME, 500),
  92. Clients = [
  93. erlang:spawn(
  94. fun() ->
  95. client(?FUNCTION_NAME, PerClient)
  96. end
  97. )
  98. || _ <- lists:seq(1, NumClients)
  99. ],
  100. {?FUNCTION_NAME, 500, Clients};
  101. size_500_clients_50_take_return_all({input, {_Pool, _Size, _Clients}}) ->
  102. [];
  103. size_500_clients_50_take_return_all({stop, {PoolName, _Size, Clients}}) ->
  104. [exit(Pid, shutdown) || Pid <- Clients],
  105. stop(PoolName).
  106. bench_size_500_clients_50_take_return_all(_Input, {_PoolName, _Size, Clients}) ->
  107. Ref = erlang:make_ref(),
  108. Self = self(),
  109. lists:foreach(fun(C) -> C ! {do, Self, Ref} end, Clients),
  110. lists:foreach(
  111. fun(_C) ->
  112. receive
  113. {done, RecRef} ->
  114. RecRef = Ref
  115. after 5000 ->
  116. error(timeout)
  117. end
  118. end,
  119. Clients
  120. ).
  121. %% @doc Artificial example: pool with init_count=500, max_count=500 that is culled to 0 on each iteration.
  122. %% Try to take 500 workers sequentially and instantly return them (and trigger culling).
  123. %% This benchmark, while is quite unrealistic (why have pool that instantly kills workers), but it
  124. %% helps to test worker spawn/kill routines.
  125. size_0_max_500_take_return_all(init) ->
  126. Size = 500,
  127. start([
  128. {name, ?FUNCTION_NAME},
  129. {init_count, 0},
  130. {max_count, Size},
  131. {max_age, {0, sec}}
  132. ]),
  133. {?FUNCTION_NAME, Size};
  134. size_0_max_500_take_return_all({input, {_Pool, _Size}}) ->
  135. [];
  136. size_0_max_500_take_return_all({stop, {PoolName, _}}) ->
  137. stop(PoolName).
  138. bench_size_0_max_500_take_return_all(_Input, {PoolName, Size}) ->
  139. Members = take_n(PoolName, 500, Size),
  140. [pooler:return_member(PoolName, Member) || Member <- Members],
  141. whereis(PoolName) ! cull_pool,
  142. Utilization = pooler:pool_utilization(PoolName),
  143. 0 = proplists:get_value(free_count, Utilization),
  144. 0 = proplists:get_value(in_use_count, Utilization).
  145. %% Internal
  146. start_fixed(Name, Size) ->
  147. Conf = [
  148. {name, Name},
  149. {init_count, Size},
  150. {max_count, Size}
  151. ],
  152. start(Conf).
  153. start(Conf0) ->
  154. Conf = [{start_mfa, {pooled_gs, start_link, [{"test"}]}} | Conf0],
  155. logger:set_handler_config(default, filters, []),
  156. {ok, _} = application:ensure_all_started(pooler),
  157. {ok, _} = pooler:new_pool(Conf).
  158. stop(Name) ->
  159. pooler:rm_pool(Name),
  160. application:stop(pooler).
  161. take_n(PoolName, N) ->
  162. take_n(PoolName, 0, N).
  163. take_n(_, _, 0) ->
  164. [];
  165. take_n(PoolName, Timeout, N) ->
  166. Member = pooler:take_member(PoolName, Timeout),
  167. true = is_pid(Member),
  168. [Member | take_n(PoolName, Timeout, N - 1)].
  169. client(Pool, N) ->
  170. receive
  171. {do, From, Ref} ->
  172. Taken = take_n(Pool, N),
  173. [pooler:return_member(Pool, Member) || Member <- Taken],
  174. From ! {done, Ref}
  175. end,
  176. client(Pool, N).