summaryrefslogtreecommitdiff
path: root/lib/ssl/test/ssl_dist_bench_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/test/ssl_dist_bench_SUITE.erl')
-rw-r--r--lib/ssl/test/ssl_dist_bench_SUITE.erl876
1 files changed, 695 insertions, 181 deletions
diff --git a/lib/ssl/test/ssl_dist_bench_SUITE.erl b/lib/ssl/test/ssl_dist_bench_SUITE.erl
index b556701869..989007bec0 100644
--- a/lib/ssl/test/ssl_dist_bench_SUITE.erl
+++ b/lib/ssl/test/ssl_dist_bench_SUITE.erl
@@ -1,7 +1,7 @@
%%%-------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2017-2022. All Rights Reserved.
+%% Copyright Ericsson AB 2017-2023. 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.
@@ -18,6 +18,7 @@
%% %CopyrightEnd%
%%
-module(ssl_dist_bench_SUITE).
+-feature(maybe_expr, enable).
-behaviour(ct_suite).
@@ -33,8 +34,10 @@
%% Test cases
-export(
[setup/1,
+ parallel_setup/1,
roundtrip/1,
sched_utilization/1,
+ mean_load_cpu_margin/1,
throughput_0/1,
throughput_64/1,
throughput_1024/1,
@@ -45,27 +48,44 @@
throughput_1048576/1]).
%% Debug
--export([payload/1, roundtrip_runner/3, setup_runner/3, throughput_runner/4]).
+-export([payload/1, roundtrip_runner/3, setup_runner/3, throughput_runner/4,
+ mem/0]).
%%%-------------------------------------------------------------------
suite() -> [{ct_hooks, [{ts_install_cth, [{nodenames, 2}]}]}].
all() ->
- [{group, ssl},
- {group, crypto},
- {group, plain}].
+ [{group, smoketest}].
groups() ->
- [{benchmark, all()},
+ [{smoketest, protocols()},
+ {benchmark, protocols()},
+ %%
+ %% protocols()
+ {ssl, ssl_backends()},
+ {cryptcookie, cryptcookie_backends()},
+ {plain, categories()},
+ {socket, categories()},
+ %%
+ %% ssl_backends()
+ {tls, categories()},
+ {ktls, categories()},
%%
- {ssl, all_groups()},
- {crypto, all_groups()},
- {plain, all_groups()},
+ %% cryptcookie_backends()
+ {dist_cryptcookie_socket, categories()},
+ {cryptcookie_socket_ktls, categories()},
+ {dist_cryptcookie_inet, categories()},
+ {cryptcookie_inet_ktls, categories()},
%%
- {setup, [{repeat, 1}], [setup]},
+ %% categories()
+ {setup, [{repeat, 1}],
+ [setup,
+ parallel_setup]},
{roundtrip, [{repeat, 1}], [roundtrip]},
- {sched_utilization,[{repeat, 1}], [sched_utilization]},
+ {sched_utilization,[{repeat, 1}],
+ [sched_utilization,
+ mean_load_cpu_margin]},
{throughput, [{repeat, 1}],
[throughput_0,
throughput_64,
@@ -76,7 +96,23 @@ groups() ->
throughput_262144,
throughput_1048576]}].
-all_groups() ->
+protocols() ->
+ [{group, ssl},
+ {group, cryptcookie},
+ {group, plain},
+ {group, socket}].
+
+ssl_backends() ->
+ [{group, tls},
+ {group, ktls}].
+
+cryptcookie_backends() ->
+ [{group, dist_cryptcookie_socket},
+ {group, cryptcookie_socket_ktls},
+ {group, dist_cryptcookie_inet},
+ {group, cryptcookie_inet_ktls}].
+
+categories() ->
[{group, setup},
{group, roundtrip},
{group, throughput},
@@ -84,31 +120,37 @@ all_groups() ->
].
init_per_suite(Config) ->
- Digest = sha1,
+ Digest = sha256,
ECCurve = secp521r1,
- TLSVersion = 'tlsv1.2',
+%%% TLSVersion = 'tlsv1.2',
+%%% TLSCipher =
+%%% #{key_exchange => ecdhe_ecdsa,
+%%% cipher => aes_128_cbc,
+%%% mac => sha256,
+%%% prf => sha256},
+ TLSVersion = 'tlsv1.3',
TLSCipher =
#{key_exchange => ecdhe_ecdsa,
- cipher => aes_128_cbc,
- mac => sha256,
- prf => sha256},
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384},
%%
Node = node(),
- Skipped = make_ref(),
+ Skip = make_ref(),
try
Node =/= nonode@nohost orelse
- throw({Skipped,"Node not distributed"}),
+ throw({Skip,"Node not distributed"}),
verify_node_src_addr(),
{supported, SSLVersions} =
lists:keyfind(supported, 1, ssl:versions()),
lists:member(TLSVersion, SSLVersions) orelse
throw(
- {Skipped,
+ {Skip,
"SSL does not support " ++ term_to_string(TLSVersion)}),
- lists:member(ECCurve, ssl:eccs(TLSVersion)) orelse
- throw(
- {Skipped,
- "SSL does not support " ++ term_to_string(ECCurve)}),
+%%% lists:member(ECCurve, ssl:eccs(TLSVersion)) orelse
+%%% throw(
+%%% {Skip,
+%%% "SSL does not support " ++ term_to_string(ECCurve)}),
TLSCipherKeys = maps:keys(TLSCipher),
lists:any(
fun (Cipher) ->
@@ -116,25 +158,11 @@ init_per_suite(Config) ->
end,
ssl:cipher_suites(default, TLSVersion)) orelse
throw(
- {Skipped,
+ {Skip,
"SSL does not support " ++ term_to_string(TLSCipher)}),
%%
%%
%%
- PrivDir = proplists:get_value(priv_dir, Config),
- [_, HostA] = split_node(Node),
- NodeAName = ?MODULE_STRING ++ "_node_a",
- NodeAString = NodeAName ++ "@" ++ HostA,
- NodeAConfFile = filename:join(PrivDir, NodeAString ++ ".conf"),
- NodeA = list_to_atom(NodeAString),
- %%
- ServerNode = ssl_bench_test_lib:setup(dist_server),
- [_, HostB] = split_node(ServerNode),
- NodeBName = ?MODULE_STRING ++ "_node_b",
- NodeBString = NodeBName ++ "@" ++ HostB,
- NodeBConfFile = filename:join(PrivDir, NodeBString ++ ".conf"),
- NodeB = list_to_atom(NodeBString),
- %%
CertOptions =
[{digest, Digest},
{key, {namedCurve, ECCurve}}],
@@ -152,56 +180,153 @@ init_per_suite(Config) ->
| SSLConf],
ClientConf = SSLConf,
%%
+ PrivDir = proplists:get_value(priv_dir, Config),
+ %%
+ ServerNode = ssl_bench_test_lib:setup(dist_server),
+ [_, ServerHost] = split_node(ServerNode),
+ ServerName = ?MODULE_STRING ++ "_server",
+ ServerString = ServerName ++ "@" ++ ServerHost,
+ ServerConfFile = filename:join(PrivDir, ServerString ++ ".conf"),
+ Server = list_to_atom(ServerString),
+ %%
write_node_conf(
- NodeAConfFile, NodeA, ServerConf, ClientConf,
- CertOptions, RootCert),
- write_node_conf(
- NodeBConfFile, NodeB, ServerConf, ClientConf,
+ ServerConfFile, Server, ServerConf, ClientConf,
CertOptions, RootCert),
%%
- [{node_a_name, NodeAName},
- {node_a, NodeA},
- {node_a_dist_args,
+ Schedulers =
+ erpc:call(ServerNode, erlang, system_info, [schedulers]),
+ [_, ClientHost] = split_node(Node),
+ [{server_node, ServerNode},
+ {server_name, ServerName},
+ {server, Server},
+ {server_dist_args,
"-proto_dist inet_tls "
- "-ssl_dist_optfile " ++ NodeAConfFile ++ " "},
- {node_b_name, NodeBName},
- {node_b, NodeB},
- {node_b_dist_args,
- "-proto_dist inet_tls "
- "-ssl_dist_optfile " ++ NodeBConfFile ++ " "},
- {server_node, ServerNode}
- |Config]
+ "-ssl_dist_optfile " ++ ServerConfFile ++ " "},
+ {clients, Schedulers} |
+ init_client_node(
+ ClientHost, Schedulers, PrivDir, ServerConf, ClientConf,
+ CertOptions, RootCert, Config)]
catch
- throw : {Skipped, Reason} ->
- {skipped, Reason};
+ throw : {Skip, Reason} ->
+ {skip, Reason};
Class : Reason : Stacktrace ->
- {failed, {Class, Reason, Stacktrace}}
+ {fail, {Class, Reason, Stacktrace}}
end.
+init_client_node(
+ _ClientHost, 0, _PrivDir, _ServerConf, _ClientConf,
+ _CertOptions, _RootCert, Config) ->
+ Config;
+init_client_node(
+ ClientHost, N, PrivDir, ServerConf, ClientConf,
+ CertOptions, RootCert, Config) ->
+ ClientName = ?MODULE_STRING ++ "_client_" ++ integer_to_list(N),
+ ClientString = ClientName ++ "@" ++ ClientHost,
+ ClientConfFile = filename:join(PrivDir, ClientString ++ ".conf"),
+ Client = list_to_atom(ClientString),
+ %%
+ write_node_conf(
+ ClientConfFile, Client, ServerConf, ClientConf,
+ CertOptions, RootCert),
+ init_client_node(
+ ClientHost, N - 1, PrivDir, ServerConf, ClientConf,
+ CertOptions, RootCert,
+ [{{client_name, N}, ClientName},
+ {{client, N}, Client},
+ {{client_dist_args, N},
+ "-proto_dist inet_tls "
+ "-ssl_dist_optfile " ++ ClientConfFile ++ " "} | Config]).
+
end_per_suite(Config) ->
ServerNode = proplists:get_value(server_node, Config),
ssl_bench_test_lib:cleanup(ServerNode).
+init_per_group(benchmark, Config) ->
+ [{effort,10}|Config];
+%%
init_per_group(ssl, Config) ->
[{ssl_dist, true}, {ssl_dist_prefix, "SSL"}|Config];
-init_per_group(crypto, Config) ->
- try inet_crypto_dist:supported() of
+init_per_group(dist_cryptcookie_socket, Config) ->
+ try inet_epmd_dist_cryptcookie_socket:supported() of
+ ok ->
+ [{ssl_dist, false}, {ssl_dist_prefix, "Crypto-Socket"},
+ {ssl_dist_args,
+ "-proto_dist inet_epmd -inet_epmd dist_cryptcookie_socket"}
+ | Config];
+ Problem ->
+ {skip, Problem}
+ catch
+ Class : Reason : Stacktrace ->
+ {fail, {Class, Reason, Stacktrace}}
+ end;
+init_per_group(cryptcookie_socket_ktls, Config) ->
+ try inet_epmd_cryptcookie_socket_ktls:supported() of
+ ok ->
+ [{ssl_dist, false}, {ssl_dist_prefix, "Crypto-Socket-kTLS"},
+ {ssl_dist_args,
+ "-proto_dist inet_epmd -inet_epmd cryptcookie_socket_ktls"}
+ | Config];
+ Problem ->
+ {skip, Problem}
+ catch
+ Class : Reason : Stacktrace ->
+ {fail, {Class, Reason, Stacktrace}}
+ end;
+init_per_group(dist_cryptcookie_inet, Config) ->
+ try inet_epmd_dist_cryptcookie_inet:supported() of
ok ->
- [{ssl_dist, false}, {ssl_dist_prefix, "Crypto"},
+ [{ssl_dist, false}, {ssl_dist_prefix, "Crypto-Inet"},
{ssl_dist_args,
- "-proto_dist inet_crypto"}
- |Config];
+ "-proto_dist inet_epmd -inet_epmd dist_cryptcookie_inet"}
+ | Config];
Problem ->
- {skipped,
- "Crypto does not support " ++ Problem}
+ {skip, Problem}
catch
Class : Reason : Stacktrace ->
- {failed, {Class, Reason, Stacktrace}}
+ {fail, {Class, Reason, Stacktrace}}
+ end;
+init_per_group(cryptcookie_inet_ktls, Config) ->
+ try inet_epmd_cryptcookie_inet_ktls:supported() of
+ ok ->
+ [{ssl_dist, false}, {ssl_dist_prefix, "Crypto-Inet-kTLS"},
+ {ssl_dist_args,
+ "-proto_dist inet_epmd -inet_epmd cryptcookie_inet_ktls"}
+ | Config];
+ Problem ->
+ {skip, Problem}
+ catch
+ Class : Reason : Stacktrace ->
+ {fail, {Class, Reason, Stacktrace}}
end;
init_per_group(plain, Config) ->
[{ssl_dist, false}, {ssl_dist_prefix, "Plain"}|Config];
-init_per_group(benchmark, Config) ->
- [{effort,10}|Config];
+%%
+init_per_group(socket, Config) ->
+ try inet_epmd_socket:supported() of
+ ok ->
+ [{ssl_dist, false},
+ {ssl_dist_prefix, "Socket"},
+ {ssl_dist_args,
+ "-proto_dist inet_epmd -inet_epmd socket"}
+ | Config];
+ Problem ->
+ {skip, Problem}
+ catch
+ Class : Reason : Stacktrace ->
+ {fail, {Class, Reason, Stacktrace}}
+ end;
+%%
+init_per_group(ktls, Config) ->
+ case ktls_supported() of
+ ok ->
+ [{ktls, true},
+ {ssl_dist_prefix,
+ proplists:get_value(ssl_dist_prefix, Config) ++ "-kTLS"}
+ | proplists:delete(ssl_dist_prefix, Config)];
+ {error, Reason} ->
+ {skip, Reason}
+ end;
+%%
init_per_group(_GroupName, Config) ->
Config.
@@ -227,6 +352,24 @@ init_per_testcase(Func, Conf) ->
end_per_testcase(_Func, _Conf) ->
ok.
+
+ktls_supported() ->
+ {ok, Listen} = gen_tcp:listen(0, [{active, false}]),
+ {ok, Port} = inet:port(Listen),
+ {ok, Client} =
+ gen_tcp:connect({127,0,0,1}, Port, [{active, false}]),
+ try
+ maybe
+ {ok, OS} ?= ssl_test_lib:ktls_os(),
+ ok ?= ssl_test_lib:ktls_set_ulp(Client, OS),
+ ssl_test_lib:ktls_set_cipher(Client, OS, tx, 1)
+ end
+ after
+ _ = gen_tcp:close(Client),
+ _ = gen_tcp:close(Listen)
+ end.
+
+
%%%-------------------------------------------------------------------
%%% CommonTest API helpers
@@ -297,18 +440,31 @@ setup(Config) ->
run_nodepair_test(fun setup/6, Config).
setup(A, B, Prefix, Effort, HA, HB) ->
- Rounds = 5 * Effort,
+ Rounds = 100 * Effort,
[] = ssl_apply(HA, erlang, nodes, []),
[] = ssl_apply(HB, erlang, nodes, []),
+ pong = ssl_apply(HA, net_adm, ping, [B]),
+ _ = ssl_apply(HA, fun () -> set_cpu_affinity(client) end),
+ {Log, Before, After} =
+ ssl_apply(HB, fun () -> set_cpu_affinity(server) end),
+ ct:pal("Server CPU affinity: ~w -> ~w~n~s", [Before, After, Log]),
+ MemStart = mem_start(HA, HB),
+ ChildCountResult =
+ ssl_dist_test_lib:apply_on_ssl_node(
+ HA, supervisor, count_children, [tls_dist_connection_sup]),
+ ct:log("TLS Connection Child Count Result: ~p", [ChildCountResult]),
{SetupTime, CycleTime} =
ssl_apply(HA, fun () -> setup_runner(A, B, Rounds) end),
ok = ssl_apply(HB, fun () -> setup_wait_nodedown(A, 10000) end),
+ {MemA, MemB, MemSuffix} = mem_stop(HA, HB, MemStart),
%% [] = ssl_apply(HA, erlang, nodes, []),
%% [] = ssl_apply(HB, erlang, nodes, []),
SetupSpeed = round((Rounds*1000000*1000) / SetupTime),
CycleSpeed = round((Rounds*1000000*1000) / CycleTime),
+ _ = report(Prefix++" Setup Mem A", MemA, "KByte"),
+ _ = report(Prefix++" Setup Mem B", MemB, "KByte"),
_ = report(Prefix++" Setup", SetupSpeed, "setups/1000s"),
- report(Prefix++" Setup Cycle", CycleSpeed, "cycles/1000s").
+ report(Prefix++" Setup Cycle", CycleSpeed, "cycles/1000s " ++ MemSuffix).
%% Runs on node A against rex in node B
setup_runner(A, B, Rounds) ->
@@ -320,7 +476,14 @@ setup_loop(_A, _B, T, 0) ->
T;
setup_loop(A, B, T, N) ->
StartTime = start_time(),
- [N,A] = [N|rpc:block_call(B, erlang, nodes, [])],
+ try erpc:call(B, net_adm, ping, [A]) of
+ pong -> ok;
+ Other ->
+ error({N,Other})
+ catch
+ Class : Reason : Stacktrace ->
+ erlang:raise(Class, {N,Reason}, Stacktrace)
+ end,
Time = elapsed_time(StartTime),
[N,B] = [N|erlang:nodes()],
Mref = erlang:monitor(process, {rex,B}),
@@ -348,6 +511,145 @@ setup_wait_nodedown(A, Time) ->
end.
+set_cpu_affinity(client) ->
+ set_cpu_affinity(1);
+set_cpu_affinity(server) ->
+ set_cpu_affinity(2);
+set_cpu_affinity(Index) when is_integer(Index) ->
+ case erlang:system_info(cpu_topology) of
+ undefined ->
+ {"", undefined, undefined};
+ CpuTopology ->
+ Log = taskset(element(Index, split_cpus(CpuTopology))),
+ %% Update Schedulers
+ _ = erlang:system_info(update_cpu_info),
+ Schedulers = erlang:system_info(logical_processors_available),
+ {Log,
+ erlang:system_flag(schedulers_online, Schedulers),
+ Schedulers}
+ end.
+
+taskset(LogicalProcessors) ->
+ os:cmd(
+ "taskset -c -p " ++
+ lists:flatten(
+ lists:join(
+ ",",
+ [integer_to_list(Id) || Id <- LogicalProcessors]),
+ " ") ++ os:getpid()).
+
+split_cpus([{_Tag, List}]) ->
+ split_cpus(List);
+split_cpus(List = [_ | _]) ->
+ {A, B} = lists:split(length(List) bsr 1, List),
+ {logical_processors(A), logical_processors(B)}.
+
+logical_processors([{_Tag, {logical, Id}} | Items]) ->
+ [Id | logical_processors(Items)];
+logical_processors([{_Tag, List} | Items]) ->
+ logical_processors(List) ++ logical_processors(Items);
+logical_processors([]) ->
+ [].
+
+
+%%----------------
+%% Parallel setup
+
+parallel_setup(Config) ->
+ Clients = proplists:get_value(clients, Config),
+ parallel_setup(Config, Clients, Clients, []).
+
+parallel_setup(Config, Clients, I, HNs) when 0 < I ->
+ Key = {client, I},
+ Node = proplists:get_value(Key, Config),
+ Handle = start_ssl_node(Key, Config),
+ _ = ssl_apply(Handle, fun () -> set_cpu_affinity(client) end),
+ try
+ parallel_setup(Config, Clients, I - 1, [{Handle, Node} | HNs])
+ after
+ stop_ssl_node(Key, Handle, Config)
+ end;
+parallel_setup(Config, Clients, _0, HNs) ->
+ Key = server,
+ ServerNode = proplists:get_value(Key, Config),
+ ServerHandle = start_ssl_node(Key, Config, 0),
+ Effort = proplists:get_value(effort, Config, 1),
+ TotalRounds = 1000 * Effort,
+ Rounds = round(TotalRounds / Clients),
+ try
+ {Log, Before, After} =
+ ssl_apply(ServerHandle, fun () -> set_cpu_affinity(server) end),
+ ct:pal("Server CPU affinity: ~w -> ~w~n~s", [Before, After, Log]),
+ ServerMemBefore =
+ ssl_apply(ServerHandle, fun mem/0),
+ parallel_setup_result(
+ Config, TotalRounds, ServerHandle, ServerMemBefore,
+ [parallel_setup_runner(Handle, Node, ServerNode, Rounds)
+ || {Handle, Node} <- HNs])
+ after
+ stop_ssl_node(Key, ServerHandle, Config)
+ end.
+
+parallel_setup_runner(Handle, Node, ServerNode, Rounds) ->
+ Collector = self(),
+ Tag = make_ref(),
+ _ =
+ spawn_link(
+ fun () ->
+ Collector !
+ {Tag,
+ try
+ MemBefore =
+ ssl_apply(Handle, fun mem/0),
+ Result =
+ ssl_apply(
+ Handle, ?MODULE, setup_runner,
+ [Node, ServerNode, Rounds]),
+ MemAfter =
+ ssl_apply(Handle, fun mem/0),
+ {MemBefore, Result, MemAfter}
+ catch Class : Reason : Stacktrace ->
+ {Class, Reason, Stacktrace}
+ end}
+ end),
+ Tag.
+
+parallel_setup_result(
+ Config, TotalRounds, ServerHandle, ServerMemBefore, Tags) ->
+ parallel_setup_result(
+ Config, TotalRounds, ServerHandle, ServerMemBefore, Tags,
+ 0, 0, 0).
+%%
+parallel_setup_result(
+ Config, TotalRounds, ServerHandle, ServerMemBefore, [Tag | Tags],
+ SetupTime, CycleTime, Mem) ->
+ receive
+ {Tag, {Mem1, {ST, CT}, Mem2}}
+ when is_integer(ST), is_integer(CT) ->
+ parallel_setup_result(
+ Config, TotalRounds, ServerHandle, ServerMemBefore, Tags,
+ SetupTime + ST, CycleTime + CT, Mem + Mem2 - Mem1);
+ {Tag, Error} ->
+ exit(Error)
+ end;
+parallel_setup_result(
+ Config, TotalRounds, ServerHandle, ServerMemBefore, [],
+ SetupTime, CycleTime, Mem) ->
+ ServerMemAfter =
+ ssl_apply(ServerHandle, fun mem/0),
+ ServerMem = ServerMemAfter - ServerMemBefore,
+ Clients = proplists:get_value(clients, Config),
+ Prefix = proplists:get_value(ssl_dist_prefix, Config),
+ SetupSpeed = 1000 * round(TotalRounds / (SetupTime/1000000)),
+ CycleSpeed = 1000 * round(TotalRounds / (CycleTime/1000000)),
+ {MemC, MemS, MemSuffix} = mem_result({Mem / Clients, ServerMem}),
+ _ = report(Prefix++" Parallel Setup Mem Clients", MemC, "KByte"),
+ _ = report(Prefix++" Parallel Setup Mem Server", MemS, "KByte"),
+ _ = report(Prefix++" Parallel Setup", SetupSpeed, "setups/1000s"),
+ report(
+ Prefix++" Parallel Setup Cycle", CycleSpeed, "cycles/1000s "
+ ++ MemSuffix).
+
%%----------------
%% Roundtrip speed
@@ -358,28 +660,33 @@ roundtrip(A, B, Prefix, Effort, HA, HB) ->
Rounds = 4000 * Effort,
[] = ssl_apply(HA, erlang, nodes, []),
[] = ssl_apply(HB, erlang, nodes, []),
+ MemStart = mem_start(HA, HB),
ok = ssl_apply(HA, net_kernel, allow, [[B]]),
ok = ssl_apply(HB, net_kernel, allow, [[A]]),
Time = ssl_apply(HA, fun () -> roundtrip_runner(A, B, Rounds) end),
[B] = ssl_apply(HA, erlang, nodes, []),
[A] = ssl_apply(HB, erlang, nodes, []),
+ {MemA, MemB, MemSuffix} = mem_stop(HA, HB, MemStart),
Speed = round((Rounds*1000000) / Time),
- report(Prefix++" Roundtrip", Speed, "pings/s").
+ _ = report(Prefix++" Roundtrip Mem A", MemA, "KByte"),
+ _ = report(Prefix++" Roundtrip Mem B", MemB, "KByte"),
+ report(Prefix++" Roundtrip", Speed, "pings/s " ++ MemSuffix).
%% Runs on node A and spawns a server on node B
roundtrip_runner(A, B, Rounds) ->
ClientPid = self(),
- [A] = rpc:call(B, erlang, nodes, []),
+ [A] = erpc:call(B, erlang, nodes, []),
ServerPid =
erlang:spawn(
B,
- fun () -> roundtrip_server(ClientPid, Rounds) end),
+ fun () ->
+ roundtrip_server(ClientPid, Rounds)
+ end),
ServerMon = erlang:monitor(process, ServerPid),
- microseconds(
- roundtrip_client(ServerPid, ServerMon, start_time(), Rounds)).
+ roundtrip_client(ServerPid, ServerMon, start_time(), Rounds).
roundtrip_server(_Pid, 0) ->
- ok;
+ exit(ok);
roundtrip_server(Pid, N) ->
receive
N ->
@@ -390,7 +697,7 @@ roundtrip_server(Pid, N) ->
roundtrip_client(_Pid, Mon, StartTime, 0) ->
Time = elapsed_time(StartTime),
receive
- {'DOWN', Mon, _, _, normal} ->
+ {'DOWN', Mon, _, _, ok} ->
Time;
{'DOWN', Mon, _, _, Other} ->
exit(Other)
@@ -416,6 +723,7 @@ sched_utilization(A, B, Prefix, Effort, HA, HB, Config) ->
SSL = proplists:get_value(ssl_dist, Config),
[] = ssl_apply(HA, erlang, nodes, []),
[] = ssl_apply(HB, erlang, nodes, []),
+ MemStart = mem_start(HA, HB),
PidA = ssl_apply(HA, os, getpid, []),
PidB = ssl_apply(HB, os, getpid, []),
ct:pal("Starting scheduler utilization run effort ~w:~n"
@@ -435,6 +743,7 @@ sched_utilization(A, B, Prefix, Effort, HA, HB, Config) ->
ct:log("Got ~p busy_dist_port msgs",[tail(BusyDistPortMsgs)]),
[B] = ssl_apply(HA, erlang, nodes, []),
[A] = ssl_apply(HB, erlang, nodes, []),
+ {MemA, MemB, MemSuffix} = mem_stop(HA, HB, MemStart),
ct:log("Microstate accounting for node ~w:", [A]),
msacc:print(ClientMsacc),
ct:log("Microstate accounting for node ~w:", [B]),
@@ -459,26 +768,30 @@ sched_utilization(A, B, Prefix, Effort, HA, HB, Config) ->
ct:log("Stray Msgs: ~p", [BusyDistPortMsgs]),
" ???"
end,
+ _ = report(Prefix++" Sched Utilization Client Mem", MemA, "KByte"),
+ _ = report(Prefix++" Sched Utilization Server Mem", MemB, "KByte"),
{comment, ClientComment} =
report(Prefix ++ " Sched Utilization Client" ++ Verdict,
- SchedUtilClient, "/100 %" ++ Verdict),
+ SchedUtilClient, " %" ++ Verdict),
{comment, ServerComment} =
report(Prefix++" Sched Utilization Server" ++ Verdict,
- SchedUtilServer, "/100 %" ++ Verdict),
- {comment, "Client " ++ ClientComment ++ ", Server " ++ ServerComment}.
+ SchedUtilServer, " %" ++ Verdict),
+ {comment,
+ "Client " ++ ClientComment ++ ", Server " ++ ServerComment ++
+ " " ++ MemSuffix}.
%% Runs on node A and spawns a server on node B
%% We want to avoid getting busy_dist_port as it hides the true SU usage
%% of the receiver and sender.
sched_util_runner(A, B, Effort, true, Config) ->
- sched_util_runner(A, B, Effort, 250, Config);
+ sched_util_runner(A, B, Effort, 100, Config);
sched_util_runner(A, B, Effort, false, Config) ->
- sched_util_runner(A, B, Effort, 250, Config);
+ sched_util_runner(A, B, Effort, 100, Config);
sched_util_runner(A, B, Effort, Senders, Config) ->
process_flag(trap_exit, true),
- Payload = payload(5),
+ Payload = payload(100),
Time = 1000 * Effort,
- [A] = rpc:call(B, erlang, nodes, []),
+ [A] = erpc:call(B, erlang, nodes, []),
ServerPids =
[erlang:spawn_link(
B, fun () -> throughput_server() end)
@@ -514,8 +827,9 @@ sched_util_runner(A, B, Effort, Senders, Config) ->
end
end),
erlang:system_monitor(self(),[busy_dist_port]),
- %% We spawn 250 senders which should mean that we
- %% have a load of 25 msgs/msec
+ %% We spawn 100 senders that send a message every 10 ms
+ %% which should produce a load of 10000 msgs/s with
+ %% payload 100 bytes each -> 1 MByte/s
_Clients =
[spawn_link(
fun() ->
@@ -594,6 +908,146 @@ throughput_client(Pid, Payload) ->
receive after 10 -> throughput_client(Pid, Payload) end.
%%-----------------
+%% Mean load CPU margin
+%%
+%% Start pairs of processes with the client on node A
+%% and the server on node B. The clients sends requests
+%% with random interval and payload and the servers reply
+%% immediately.
+%%
+%% Also, besides each server there is a compute process
+%% that does CPU work with low process priority and we measure
+%% how much such work that gets done.
+
+mean_load_cpu_margin(Config) ->
+ run_nodepair_test(fun run_mlcm/6, Config).
+
+-define(MLCM_NO, 100).
+
+run_mlcm(A, B, Prefix, Effort, HA, HB) ->
+ [] = ssl_apply(HA, erlang, nodes, []),
+ [] = ssl_apply(HB, erlang, nodes, []),
+ MemStart = mem_start(HA, HB),
+ pong = ssl_apply(HB, net_adm, ping, [A]),
+ Count = ssl_apply(HA, fun () -> mlcm(B, Effort) end),
+ {MemA, MemB, MemSuffix} = mem_stop(HA, HB, MemStart),
+ _ = report(Prefix++" CPU Margin Mem A", MemA, "KByte"),
+ _ = report(Prefix++" CPU Margin Mem B", MemB, "KByte"),
+ report(
+ Prefix++" CPU Margin",
+ round(Count/?MLCM_NO/Effort),
+ "stones " ++ MemSuffix).
+
+mlcm(Node, Effort) ->
+ Payloads = mlcm_payloads(),
+ Clients =
+ [mlcm_client_start(Node, Payloads) || _ <- lists:seq(1, ?MLCM_NO)],
+ receive after 1000 * Effort -> ok end,
+ [Alias ! {Alias,stop} || {_Monitor, Alias} <- Clients],
+ Counts =
+ [receive
+ {'DOWN',Monitor,_,_,{Alias, Count}} ->
+ Count;
+ {'DOWN',Monitor,_,_,Reason} ->
+ exit(Reason)
+ end || {Monitor, Alias} <- Clients],
+ lists:sum(Counts).
+
+mlcm_payloads() ->
+ Bin = list_to_binary([rand:uniform(256) - 1 || _ <- lists:seq(1, 512)]),
+ lists:foldl(
+ fun (N, Payloads) ->
+ Payloads#{N => binary:copy(Bin, N)}
+ end, #{}, lists:seq(0, 255)).
+
+%%-------
+
+mlcm_client_start(Node, Payloads) ->
+ Parent = self(),
+ StartRef = make_ref(),
+ {_,Monitor} =
+ spawn_monitor(
+ fun () ->
+ Alias = alias(),
+ Parent ! {StartRef, Alias},
+ Server = mlcm_server_start(Node, Alias),
+ mlcm_client(Alias, Server, Payloads, 0)
+ end),
+ receive
+ {StartRef, Alias} ->
+ {Monitor, Alias};
+ {'DOWN',Monitor,_,_,Reason} ->
+ exit(Reason)
+ end.
+
+mlcm_client(Alias, Server, Payloads, Seq) ->
+ {Time, Index} = mlcm_rand(),
+ Payload = maps:get(Index, Payloads),
+ receive after Time -> ok end,
+ Server ! {Alias, Seq, Payload},
+ receive
+ {Alias, Seq, Pl} when byte_size(Pl) =:= byte_size(Payload) ->
+ mlcm_client(Alias, Server, Payloads, Seq + 1);
+ {Alias, stop} = Msg ->
+ Server ! Msg,
+ receive after infinity -> ok end
+ end.
+
+%% Approximate normal distribution Index with an average of 6 uniform bytes
+%% and use the 7:th byte for uniform Time
+mlcm_rand() ->
+ mlcm_rand(6, rand:uniform(1 bsl (1+6)*8) - 1, 0).
+%%
+mlcm_rand(0, X, I) ->
+ Time = X + 1, % 1..256
+ Index = abs((I - 3*256) div 3), % 0..255 upper half or normal distribution
+ {Time, Index};
+mlcm_rand(N, X, I) ->
+ mlcm_rand(N - 1, X bsr 8, I + (X band 255)).
+
+%%-------
+
+mlcm_server_start(Node, Alias) ->
+ spawn_link(
+ Node,
+ fun () ->
+ Compute = mlcm_compute_start(Alias),
+ mlcm_server(Alias, 0, Compute)
+ end).
+
+mlcm_server(Alias, Seq, Compute) ->
+ receive
+ {Alias, Seq, _Payload} = Msg ->
+ Alias ! Msg,
+ mlcm_server(Alias, Seq + 1, Compute);
+ {Alias, stop} = Msg ->
+ Compute ! Msg,
+ receive after infinity -> om end
+ end.
+
+%%-------
+
+mlcm_compute_start(Alias) ->
+ spawn_opt(
+ fun () ->
+ rand:seed(exro928ss),
+ mlcm_compute(Alias, 0, 0)
+ end,
+ [link, {priority,low}]).
+
+mlcm_compute(Alias, State, Count) ->
+ receive {Alias, stop} -> exit({Alias, Count})
+ after 0 -> ok
+ end,
+ mlcm_compute(
+ Alias,
+ %% CPU payload
+ (State +
+ lists:sum([rand:uniform(1 bsl 48) || _ <- lists:seq(1, 999)]))
+ div 1000,
+ Count + 1).
+
+%%-----------------
%% Throughput speed
throughput_0(Config) ->
@@ -647,8 +1101,8 @@ throughput_1048576(Config) ->
throughput(A, B, Prefix, HA, HB, Packets, Size) ->
[] = ssl_apply(HA, erlang, nodes, []),
[] = ssl_apply(HB, erlang, nodes, []),
+ MemStart = mem_start(HA, HB),
#{time := Time,
- client_dist_stats := ClientDistStats,
client_msacc_stats := ClientMsaccStats,
client_prof := ClientProf,
server_msacc_stats := ServerMsaccStats,
@@ -658,15 +1112,19 @@ throughput(A, B, Prefix, HA, HB, Packets, Size) ->
ssl_apply(HA, fun () -> throughput_runner(A, B, Packets, Size) end),
[B] = ssl_apply(HA, erlang, nodes, []),
[A] = ssl_apply(HB, erlang, nodes, []),
+ {MemA, MemB, MemSuffix} = mem_stop(HA, HB, MemStart),
ClientMsaccStats =:= undefined orelse
msacc:print(ClientMsaccStats),
- io:format("ClientDistStats: ~p~n", [ClientDistStats]),
Overhead =
50 % Distribution protocol headers (empirical) (TLS+=54)
+ byte_size(erlang:term_to_binary([0|<<>>])), % Benchmark overhead
Bytes = Packets * (Size + Overhead),
io:format("~w bytes, ~.4g s~n", [Bytes,Time/1000000]),
SizeString = integer_to_list(Size),
+ _ = report(
+ Prefix++" Throughput_" ++ SizeString ++ " Mem A", MemA, "KByte"),
+ _ = report(
+ Prefix++" Throughput_" ++ SizeString ++ " Mem B", MemB, "KByte"),
ClientMsaccStats =:= undefined orelse
report(
Prefix ++ " Sender_RelativeCoreLoad_" ++ SizeString,
@@ -687,12 +1145,13 @@ throughput(A, B, Prefix, HA, HB, Packets, Size) ->
io:format("******* Server GC Before:~n~p~n", [Server_GC_Before]),
io:format("******* Server GC After:~n~p~n", [Server_GC_After]),
Speed = round((Bytes * 1000000) / (1024 * Time)),
- report(Prefix ++ " Throughput_" ++ SizeString, Speed, "kB/s").
+ report(
+ Prefix ++ " Throughput_" ++ SizeString, Speed, "kB/s " ++ MemSuffix).
%% Runs on node A and spawns a server on node B
throughput_runner(A, B, Rounds, Size) ->
Payload = payload(Size),
- [A] = rpc:call(B, erlang, nodes, []),
+ [A] = erpc:call(B, erlang, nodes, []),
ClientPid = self(),
ServerPid =
erlang:spawn_opt(
@@ -721,74 +1180,10 @@ throughput_runner(A, B, Rounds, Size) ->
undefined
end,
Prof = prof_end(),
- [{_Node,Socket}] = dig_dist_node_sockets(),
- DistStats = inet:getstat(Socket),
Result#{time := microseconds(Time),
- client_dist_stats => DistStats,
client_msacc_stats => MsaccStats,
client_prof => Prof}.
-dig_dist_node_sockets() ->
- DistCtrl2Node =
- maps:from_list(
- [{DistCtrl, Node}
- || {Node, DistCtrl}
- <- erlang:system_info(dist_ctrl), is_pid(DistCtrl)]),
- TlsDistConnSup = whereis(tls_dist_connection_sup),
- InetCryptoDist = whereis(inet_crypto_dist),
- [NodeSocket
- || {_, Socket} = NodeSocket
- <- erlang:system_info(dist_ctrl), is_port(Socket)]
- ++
- if
- TlsDistConnSup =/= undefined ->
- [case ConnSpec of
- {undefined, ConnSup, supervisor, _} ->
- [{receiver, ReceiverPid, worker, _},
- {sender, SenderPid, worker, _}] =
- lists:sort(supervisor:which_children(ConnSup)),
- {links,ReceiverLinks} =
- process_info(ReceiverPid, links),
- [Socket] = [S || S <- ReceiverLinks, is_port(S)],
- {maps:get(SenderPid, DistCtrl2Node), Socket}
- end
- || ConnSpec <- supervisor:which_children(TlsDistConnSup)];
- InetCryptoDist =/= undefined ->
- [begin
- {monitors,[{process,InputHandler}]} =
- erlang:process_info(DistCtrl, monitors),
- {links,InputHandlerLinks} =
- erlang:process_info(InputHandler, links),
- [Socket] =
- [S || S <- InputHandlerLinks, is_port(S)],
- {Node, Socket}
- end
- || {DistCtrl, Node} <- maps:to_list(DistCtrl2Node)];
- true ->
- []
- end.
-
--ifdef(undefined).
-dig_dist_node_sockets() ->
- [case DistCtrl of
- {_Node,Socket} = NodeSocket when is_port(Socket) ->
- NodeSocket;
- {Node,DistCtrlPid} when is_pid(DistCtrlPid) ->
- [{links,DistCtrlLinks}] = process_info(DistCtrlPid, [links]),
- case [S || S <- DistCtrlLinks, is_port(S)] of
- [Socket] ->
- {Node,Socket};
- [] ->
- [{monitors,[{process,DistSenderPid}]}] =
- process_info(DistCtrlPid, [monitors]),
- [{links,DistSenderLinks}] =
- process_info(DistSenderPid, [links]),
- [Socket] = [S || S <- DistSenderLinks, is_port(S)],
- {Node,Socket}
- end
- end || DistCtrl <- erlang:system_info(dist_ctrl)].
--endif.
-
throughput_server(Pid, N) ->
GC_Before = get_server_gc_info(),
%% dbg:tracer(port, dbg:trace_port(file, "throughput_server_gc.log")),
@@ -917,17 +1312,19 @@ prof_print([]) ->
%%% Test cases helpers
run_nodepair_test(TestFun, Config) ->
- A = proplists:get_value(node_a, Config),
- B = proplists:get_value(node_b, Config),
+ A = proplists:get_value({client,1}, Config),
+ B = proplists:get_value(server, Config),
Prefix = proplists:get_value(ssl_dist_prefix, Config),
Effort = proplists:get_value(effort, Config, 1),
- HA = start_ssl_node_a(Config),
- HB = start_ssl_node_b(Config),
- try TestFun(A, B, Prefix, Effort, HA, HB)
+ HA = start_ssl_node({client,1}, Config),
+ try
+ HB = start_ssl_node(server, Config),
+ try TestFun(A, B, Prefix, Effort, HA, HB)
+ after
+ stop_ssl_node(server, HB, Config)
+ end
after
- stop_ssl_node_a(HA),
- stop_ssl_node_b(HB, Config),
- ok
+ stop_ssl_node({client,1}, HA, Config)
end.
ssl_apply(Handle, M, F, Args) ->
@@ -946,33 +1343,39 @@ ssl_apply(Handle, Fun) ->
Result
end.
-start_ssl_node_a(Config) ->
- Name = proplists:get_value(node_a_name, Config),
- Args = get_node_args(node_a_dist_args, Config),
+start_ssl_node(Spec, Config) ->
+ start_ssl_node(Spec, Config, 0).
+%%
+start_ssl_node({client, N}, Config, Verbose) ->
+ Name = proplists:get_value({client_name, N}, Config),
+ Args = get_node_args({client_dist_args, N}, Config),
Pa = filename:dirname(code:which(?MODULE)),
ssl_dist_test_lib:start_ssl_node(
- Name, "-pa " ++ Pa ++ " " ++ Args).
-
-start_ssl_node_b(Config) ->
- Name = proplists:get_value(node_b_name, Config),
- Args = get_node_args(node_b_dist_args, Config),
+ Name, "-pa " ++ Pa ++ " +Muacul 0 " ++ Args, Verbose);
+start_ssl_node(server, Config, Verbose) ->
+ Name = proplists:get_value(server_name, Config),
+ Args = get_node_args(server_dist_args, Config),
Pa = filename:dirname(code:which(?MODULE)),
ServerNode = proplists:get_value(server_node, Config),
- rpc:call(
+ erpc:call(
ServerNode, ssl_dist_test_lib, start_ssl_node,
- [Name, "-pa " ++ Pa ++ " " ++ Args]).
+ [Name, "-pa " ++ Pa ++ " +Muacul 0 " ++ Args, Verbose]).
-stop_ssl_node_a(HA) ->
- ssl_dist_test_lib:stop_ssl_node(HA).
-
-stop_ssl_node_b(HB, Config) ->
+stop_ssl_node({client, _}, HA, _Config) ->
+ ssl_dist_test_lib:stop_ssl_node(HA);
+stop_ssl_node(server, HB, Config) ->
ServerNode = proplists:get_value(server_node, Config),
- rpc:call(ServerNode, ssl_dist_test_lib, stop_ssl_node, [HB]).
+ erpc:call(ServerNode, ssl_dist_test_lib, stop_ssl_node, [HB]).
get_node_args(Tag, Config) ->
case proplists:get_value(ssl_dist, Config) of
true ->
- proplists:get_value(Tag, Config);
+ case proplists:get_value(ktls, Config, false) of
+ true ->
+ "-ssl_dist_opt client_ktls true server_ktls true ";
+ false ->
+ ""
+ end ++ proplists:get_value(Tag, Config);
false ->
proplists:get_value(ssl_dist_args, Config, "")
end.
@@ -1018,13 +1421,13 @@ elapsed_time(StartTime) ->
microseconds(Time) ->
erlang:convert_time_unit(Time, native, microsecond).
-report(Name, Value, Unit) ->
- ct:pal("~s: ~w ~s", [Name, Value, Unit]),
+report(Name, Value, Suffix) ->
+ ct:pal("~s: ~w ~s", [Name, Value, Suffix]),
ct_event:notify(
#event{
name = benchmark_data,
data = [{value, Value}, {suite, "ssl_dist"}, {name, Name}]}),
- {comment, term_to_string(Value) ++ " " ++ Unit}.
+ {comment, term_to_string(Value) ++ " " ++ Suffix}.
term_to_string(Term) ->
unicode:characters_to_list(
@@ -1032,3 +1435,114 @@ term_to_string(Term) ->
msacc_available() ->
msacc:available().
+
+
+mem_start(HA, HB) ->
+ MemA = ssl_apply(HA, fun mem/0),
+ MemB = ssl_apply(HB, fun mem/0),
+ {MemA, MemB}.
+
+mem_stop(HA, HB, Mem1) ->
+ MemA2 = ssl_apply(HA, fun mem/0),
+ MemB2 = ssl_apply(HB, fun mem/0),
+ mem_result(mem_diff(Mem1, {MemA2, MemB2})).
+
+mem_diff({MemA1, MemB1}, {MemA2, MemB2}) ->
+ {MemA2 - MemA1, MemB2 - MemB1}.
+
+mem_result({MemDiffA, MemDiffB}) ->
+ MemSuffix =
+ io_lib:format(
+ "~.5g|~.5g MByte", [MemDiffA / (1 bsl 20), MemDiffB / (1 bsl 20)]),
+ {round(MemDiffA / (1 bsl 10)), round(MemDiffB / (1 bsl 10)), MemSuffix}.
+
+memory(Type) ->
+ try erlang:memory(Type)
+ catch error : notsup ->
+ 0
+ end.
+
+-ifdef(undefined).
+
+mem() ->
+ lists:foldl(
+ fun ({Type, F}, Acc) ->
+ F*memory(Type) + Acc
+ end, 0,
+ [{total, 1}, {processes, -1}, {atom, -1}, {code, -1},
+ {processes_used, 1}, {atom_used, 1}]).
+
+-else.
+
+mem() ->
+ {_Current, _MaxSince, MaxEver} =
+ traverse(
+ fun mem/3,
+ [erlang:system_info({allocator_sizes, Alloc})
+ || Alloc <- erlang:system_info(alloc_util_allocators)],
+ {0, 0, 0}),
+ MaxEver - memory(code). % Kind of assuming code stays allocated
+
+%% allocator_sizes traversal fun
+mem(
+ T = {instance, _, L}, [], Acc)
+ when is_list(L) ->
+ {tuple_size(T), Acc};
+mem(
+ T = {_, L}, [{instance, _, _}], Acc)
+ when is_list(L) ->
+ {tuple_size(T), Acc};
+mem(
+ T = {blocks, L}, [{_, _}, {instance, _, _}], Acc)
+ when is_list(L) ->
+ {tuple_size(T), Acc};
+mem(
+ T = {_, L}, [{blocks, _}, {_, _}, {instance, _, _}], Acc)
+ when is_list(L) ->
+ {tuple_size(T), Acc};
+mem(
+ {size, Current, MaxSince, MaxEver},
+ [{_, _}, {blocks, _}, {_, _}, {instance, _, _}],
+ {C, S, E}) ->
+ {0, {C + Current, S + MaxSince, E + MaxEver}};
+mem(
+ {size, Current},
+ [{_, _}, {blocks, _}, {_, _}, {instance, _, _}],
+ {C, S, E}) ->
+ %% Use Current as Max since we do not have any Max values
+ %% XXX future improvement when that gets added to
+ %% erlang:system_info(allocator_sizes, _)
+ {0, {C + Current, S + Current, E + Current}};
+mem(_, _, Acc) ->
+ {0, Acc}.
+
+%% Traverse (Fold) over all lists in a deep term;
+%% descend into the selected element of a tuple;
+%% record the descent Path and supply it to Fun
+%%
+%% Acc cannot be an integer
+traverse(Fun, Term, Acc) ->
+ traverse(Fun, Term, [], Acc).
+%%
+traverse(Fun, Term, Path, Acc) ->
+ if
+ is_list(Term) ->
+ traverse_list(Fun, Term, Path, Acc);
+ is_tuple(Term) ->
+ case Fun(Term, Path, Acc) of
+ {0, NewAcc} ->
+ NewAcc;
+ {N, NewAcc} when is_integer(N) ->
+ traverse(Fun, element(N, Term), [Term | Path], NewAcc)
+ end;
+ true ->
+ Acc
+ end.
+
+traverse_list(Fun, [Term | Terms], Path, Acc) ->
+ NewAcc = traverse(Fun, Term, Path, Acc),
+ traverse_list(Fun, Terms, Path, NewAcc);
+traverse_list(_Fun, [], _Path, Acc) ->
+ Acc.
+
+-endif.