chore: add emqx_broker_bench.erl

This commit is contained in:
Zaiming Shi 2021-04-25 19:15:13 +02:00 committed by Zaiming (Stone) Shi
parent 910449e67b
commit d926800204
2 changed files with 118 additions and 2 deletions

View File

@ -106,8 +106,9 @@ test_deps() ->
common_compile_opts() ->
[ debug_info % alwyas include debug_info
, {compile_info, [{emqx_vsn, get_vsn()}]}
| [{d, 'EMQX_ENTERPRISE'} || is_enterprise()]
].
] ++
[{d, 'EMQX_ENTERPRISE'} || is_enterprise()] ++
[{d, 'EMQX_BENCHMARK'} || os:getenv("EMQX_BENCHMARK") =:= "1" ].
prod_compile_opts() ->
[ compressed

115
src/emqx_broker_bench.erl Normal file
View File

@ -0,0 +1,115 @@
%%--------------------------------------------------------------------
%% Copyright (c) 2021 EMQ Technologies Co., Ltd. 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.
%%--------------------------------------------------------------------
-module(emqx_broker_bench).
-ifdef(EMQX_BENCHMARK).
-export([start/1, run1/0, run1/2]).
run1() -> run1(4, 1000).
run1(Factor, Limit) ->
start(#{factor => Factor,
limit => Limit,
sub_ptn => <<"device/{{id}}/+/{{num}}/#">>,
pub_ptn => <<"device/{{id}}/xays/{{num}}/foo/bar/baz">>}).
%% setting fields:
%% - factor: spawn broker-pool-size * factor number of callers
%% - limit: limit the total number of topics for each caller
%% - sub_ptn: subscribe topic pattern like a/+/b/+/c/#
%% or a/+/{{id}}/{{num}}/# to generate pattern with {{id}}
%% replaced by worker id and {{num}} replaced by topic number.
%% - pub_ptn: topic pattern used to benchmark publish (match) performance
%% e.g. a/x/{{id}}/{{num}}/foo/bar
start(#{factor := Factor} = Settings) ->
BrokerPoolSize = emqx_vm:schedulers() * 2,
Pids = start_callers(BrokerPoolSize * Factor, Settings),
R = collect_results(Pids, #{subscribe => 0, match => 0}),
io:format(user, "mnesia table(s) RAM: ~p~n", [ram_bytes()]),
io:format(user, "~p~n", [erlang:memory()]),
io:format(user, "~p~n", [R]),
lists:foreach(fun(Pid) -> Pid ! stop end, Pids).
ram_bytes() ->
Wordsize = erlang:system_info(wordsize),
mnesia:table_info(emqx_trie, memory) * Wordsize +
case lists:member(emqx_trie_node, ets:all()) of
true ->
%% before 4.3
mnesia:table_info(emqx_trie_node, memory) * Wordsize;
false ->
0
end.
start_callers(0, _) -> [];
start_callers(N, Settings) ->
[start_caller(Settings#{id => N}) | start_callers(N - 1, Settings)].
collect_results([], R) -> R;
collect_results([Pid | Pids], Acc = #{subscribe := Sr, match := Mr}) ->
receive
{Pid, #{subscribe := Srd, match := Mrd}} ->
collect_results(Pids, Acc#{subscribe := Sr + Srd, match := Mr + Mrd})
end.
%% ops per second
rps(T, N) -> round(N / (T / 1000000)).
start_caller(#{id := Id, limit := N, sub_ptn := SubPtn, pub_ptn := PubPtn}) ->
Parent = self(),
proc_lib:spawn_link(
fun() ->
SubTopics = make_topics(SubPtn, Id, N),
{Ts, _} = timer:tc(fun() -> subscribe(SubTopics) end),
PubTopics = make_topics(PubPtn, Id, N),
{Tm, _} = timer:tc(fun() -> match(PubTopics) end),
_ = erlang:send(Parent, {self(), #{subscribe => rps(Ts, N), match => rps(Tm, N)}}),
receive
stop ->
ok
end
end).
match([]) -> ok;
match([Topic | Topics]) ->
_ = emqx_router:lookup_routes(Topic),
match(Topics).
subscribe([]) -> ok;
subscribe([Topic | Rest]) ->
ok = emqx_broker:subscribe(Topic),
subscribe(Rest).
make_topics(SubPtn0, Id, Limit) ->
SubPtn = emqx_topic:words(SubPtn0),
F = fun(N) -> render(Id, N, SubPtn) end,
lists:map(F, lists:seq(1, Limit)).
render(ID, N, Ptn) ->
render(ID, N, Ptn, []).
render(_ID, _N, [], Acc) ->
emqx_topic:join(lists:reverse(Acc));
render(ID, N, [<<"{{id}}">> | T], Acc) ->
render(ID, N, T, [integer_to_binary(ID) | Acc]);
render(ID, N, [<<"{{num}}">> | T], Acc) ->
render(ID, N, T, [integer_to_binary(N) | Acc]);
render(ID, N, [H | T], Acc) ->
render(ID, N, T, [H | Acc]).
-endif.