Small ddsperf enhancements

* Change default behaviour with no arguments to print help text;
  "ddsperf sanity" now gives the behaviour that it used to give when run
  with no arguments;

* Include context switch rate in output;

* Allow suffixing frequencies and sizes with standard units (so "size
  1kB" is now allowed);

* Add missing option to help text, extend help text with some additional
  informationr.

Signed-off-by: Erik Boasson <eb@ilities.com>
This commit is contained in:
Erik Boasson 2019-08-03 16:47:06 +02:00 committed by eboasson
parent ca04ac48de
commit 40ba6b207f
4 changed files with 122 additions and 34 deletions

View file

@ -212,7 +212,7 @@ This example shows a few things:
the address or the interface name). Proper use of multiple network interfaces simultaneously will
come, but is not there yet.
* ``AllowMulticast`` configures the circumstances under which multicast will be used. If the
selected interface doesn't support it, it obviously wonn't be used (``false``); but if it does
selected interface doesn't support it, it obviously won't be used (``false``); but if it does
support it, the type of the network adapter determines the default value. For a wired network, it
will use multicast for initial discovery as well as for data when there are multiple peers that
the data needs to go to (``true``); but on a WiFi network it will use it only for initial
@ -225,13 +225,13 @@ This example shows a few things:
the size of the UDP payload), and the size of the fragments into which very large samples get
split (which needs to be "a bit" less). Large values such as these typically improve performance
over the (current) default values.
* ``WhcHigh`` determines when the sender will wait for acknolwedgements from the readers because it
* ``WhcHigh`` determines when the sender will wait for acknowledgements from the readers because it
has buffered too much unacknowledged data. There is some auto-tuning, the (current) default value
is a bit small to get really high throughput.
The configurator tool ``cycloneddsconf`` can help in discovering the settings, as can the config
dump. Background information on configuring Cyclone DDS can be
found [here](https://docs/manual/config.rst).
found [here](docs/manual/config.rst).
# Trademarks

View file

@ -49,7 +49,7 @@ bool print_cputime (const struct CPUStats *s, const char *prefix, bool print_hos
{
int n = (int) strlen (s->hostname);
if (n > 100) n = 100;
pos += (size_t) snprintf (line + pos, sizeof (line) - pos, " @%*.*s:%"PRId32, n, n, s->hostname, s->pid);
pos += (size_t) snprintf (line + pos, sizeof (line) - pos, " @%*.*s:%"PRIu32, n, n, s->hostname, s->pid);
}
if (s->maxrss > 1048576)
pos += (size_t) snprintf (line + pos, sizeof (line) - pos, " rss:%.1fMB", s->maxrss / 1048576.0);
@ -58,6 +58,7 @@ bool print_cputime (const struct CPUStats *s, const char *prefix, bool print_hos
else {
/* non-sensical value -- presumably maxrss is not available */
}
pos += (size_t) snprintf (line + pos, sizeof (line) - pos, " vcsw:%"PRIu32" ivcsw:%"PRIu32, s->vcsw, s->ivcsw);
const size_t init_pos = pos;
for (uint32_t i = 0; i < s->cpu._length; i++)
{
@ -81,6 +82,8 @@ struct record_cputime_state_thr {
struct record_cputime_state {
bool supported;
dds_time_t tprev;
uint32_t vcswprev;
uint32_t ivcswprev;
size_t nthreads;
struct record_cputime_state_thr *threads;
dds_entity_t wr;
@ -118,13 +121,20 @@ bool record_cputime (struct record_cputime_state *state, const char *prefix, dds
ddsrt_rusage_t usage;
if (ddsrt_getrusage (DDSRT_RUSAGE_SELF, &usage) < 0)
{
usage.maxrss = 0;
usage.nvcsw = usage.nivcsw = 0;
}
double max = 0;
double du_skip = 0.0, ds_skip = 0.0;
const double dt = (double) (tnow - state->tprev) / 1e9;
bool some_above = false;
state->s.maxrss = (double) usage.maxrss;
state->s.vcsw = (uint32_t) ((double) (usage.nvcsw - state->vcswprev) / dt + 0.5);
state->s.ivcsw = (uint32_t) ((double) (usage.nivcsw - state->ivcswprev) / dt + 0.5);
state->vcswprev = (uint32_t) usage.nvcsw;
state->ivcswprev = (uint32_t) usage.nivcsw;
state->s.cpu._length = 0;
for (size_t i = 0; i < state->nthreads; i++)
{
@ -187,14 +197,18 @@ struct record_cputime_state *record_cputime_new (dds_entity_t wr)
}
struct record_cputime_state *state = malloc (sizeof (*state));
ddsrt_rusage_t usage;
if (ddsrt_getrusage (DDSRT_RUSAGE_SELF, &usage) < 0)
usage.nvcsw = usage.nivcsw = 0;
state->tprev = dds_time ();
state->wr = wr;
state->vcswprev = (uint32_t) usage.nvcsw;
state->ivcswprev = (uint32_t) usage.nivcsw;
state->threads = malloc ((size_t) n * sizeof (*state->threads));
state->nthreads = 0;
for (int32_t i = 0; i < n; i++)
{
struct record_cputime_state_thr * const thr = &state->threads[state->nthreads];
ddsrt_rusage_t usage;
if (ddsrt_getrusage_anythread (tids[i], &usage) < 0)
continue;
thr->tid = tids[i];

View file

@ -1524,29 +1524,36 @@ static void sigxfsz_handler (int sig __attribute__ ((unused)))
static void usage (void)
{
printf ("\
%s help\n\
%s help (this text)\n\
%s sanity (ping 1Hz)\n\
%s [OPTIONS] MODE...\n\
\n\
OPTIONS:\n\
-L allow matching with endpoints in the same process\n\
to get throughput/latency in the same ddsperf process\n\
-T KS|K32|K256|OU topic (KS is default):\n\
KS seq num, key value, sequence-of-octets\n\
K32 seq num, key value, array of 24 octets\n\
K256 seq num, key value, array of 248 octets\n\
OU seq num\n\
-L allow matching with local endpoints\n\
-n N number of key values to use for data (only for\n\
topics with a key value)\n\
-u best-effort instead of reliable\n\
-k all|N keep-all or keep-last-N for data (ping/pong is\n\
always keep-last-1)\n\
-n N number of key values to use for data (only for\n\
topics with a key value)\n\
-c subscribe to CPU stats from peers and show them\n\
-d DEV:BW report network load for device DEV with nominal\n\
bandwidth BW in bits/s (e.g., eth0:1e9)\n\
-D DUR run for at most DUR seconds\n\
-N COUNT require at least COUNT matching participants\n\
-M DUR require those participants to match within DUR seconds\n\
-R TREF timestamps in the output relative to TREF instead of\n\
process start\n\
\n\
MODE... is zero or more of:\n\
ping [R[Hz]] [size N] [waitset|listener]\n\
ping [R[Hz]] [size S] [waitset|listener]\n\
Send a ping upon receiving all expected pongs, or send a ping at\n\
rate R (optionally suffixed with Hz). The triggering mode is either\n\
rate R (optionally suffixed with Hz/kHz). The triggering mode is either\n\
a listener (default, unless -L has been specified) or a waitset.\n\
pong [waitset|listener]\n\
A \"dummy\" mode that serves two purposes: configuring the triggering.\n\
@ -1555,11 +1562,12 @@ MODE... is zero or more of:\n\
sub [waitset|listener|polling]\n\
Subscribe to data, with calls to take occurring either in a listener\n\
(default), when a waitset is triggered, or by polling at 1kHz.\n\
pub [R[Hz]] [size N] [burst N] [[ping] X%%]\n\
Publish bursts of data at rate R, optionally suffixed with Hz. If\n\
pub [R[Hz]] [size S] [burst N] [[ping] X%%]\n\
Publish bursts of data at rate R, optionally suffixed with Hz/kHz. If\n\
no rate is given or R is \"inf\", data is published as fast as\n\
possible. Each burst is a single sample by default, but can be set\n\
to larger value using \"burst N\".\n\
to larger value using \"burst N\". Sample size is controlled using\n\
\"size S\", S may be suffixed with k/M/kB/MB/KiB/MiB.\n\
If desired, a fraction of the samples can be treated as if it were a\n\
ping, for this, specify a percentage either as \"ping X%%\" (the\n\
\"ping\" keyword is optional, the %% sign is not).\n\
@ -1569,8 +1577,23 @@ MODE... is zero or more of:\n\
the last one given determines it for all) and should be either 0 (minimal,\n\
equivalent to 12) or >= 12.\n\
\n\
If no MODE specified, it defaults to a 1Hz ping + responding to any pings.\n\
", argv0, argv0);
EXIT STATUS:\n\
\n\
0 all is well\n\
1 not enough peers discovered, other matching issues, unexpected sample\n\
loss detected\n\
2 unexpected failure of some DDS operation\n\
3 incorrect arguments\n\
\n\
EXAMPLES:\n\
ddsperf pub size 1k & ddsperf sub\n\
basic throughput test with 1024-bytes large samples\n\
ddsperf ping & ddsperf pong\n\
basic latency test\n\
ddsperf -L -TOU -D10 pub sub\n\
basic throughput test within the process with tiny, keyless samples,\n\
running for 10s\n\
", argv0, argv0, argv0);
fflush (stdout);
exit (3);
}
@ -1628,18 +1651,57 @@ static int string_int_map_lookup (const struct string_int_map_elem *elems, const
return (match == SIZE_MAX) ? -1 : elems[match].value;
}
static bool set_simple_uint32 (int *xoptind, int xargc, char * const xargv[], const char *token, uint32_t *val)
struct multiplier {
const char *suffix;
int mult;
};
static const struct multiplier frequency_units[] = {
{ "Hz", 1 },
{ "kHz", 1024 },
{ NULL, 0 }
};
static const struct multiplier size_units[] = {
{ "B", 1 },
{ "k", 1024 },
{ "M", 1048576 },
{ "kB", 1024 },
{ "KiB", 1024 },
{ "MB", 1048576 },
{ "MiB", 1048576 },
{ NULL, 0 }
};
static int lookup_multiplier (const struct multiplier *units, const char *suffix)
{
while (*suffix == ' ')
suffix++;
if (*suffix == 0)
return 1;
else if (units == NULL)
return 0;
else
{
for (size_t i = 0; units[i].suffix; i++)
if (strcmp (units[i].suffix, suffix) == 0)
return units[i].mult;
return 0;
}
}
static bool set_simple_uint32 (int *xoptind, int xargc, char * const xargv[], const char *token, const struct multiplier *units, uint32_t *val)
{
if (strcmp (xargv[*xoptind], token) != 0)
return false;
else
{
unsigned x;
int pos;
int pos, mult;
if (++(*xoptind) == xargc)
error3 ("argument missing in %s specification\n", token);
if (sscanf (xargv[*xoptind], "%u%n", &x, &pos) == 1 && xargv[*xoptind][pos] == 0)
*val = x;
if (sscanf (xargv[*xoptind], "%u%n", &x, &pos) == 1 && (mult = lookup_multiplier (units, xargv[*xoptind] + pos)) > 0)
*val = x * (unsigned) mult;
else
error3 ("%s: invalid %s specification\n", xargv[*xoptind], token);
return true;
@ -1652,19 +1714,20 @@ static void set_mode_ping (int *xoptind, int xargc, char * const xargv[])
pingpongmode = SM_LISTENER;
while (*xoptind < xargc && exact_string_int_map_lookup (modestrings, "mode string", xargv[*xoptind], false) == -1)
{
int pos;
int pos = 0, mult = 1;
double ping_rate;
if (strcmp (xargv[*xoptind], "inf") == 0)
if (strcmp (xargv[*xoptind], "inf") == 0 && lookup_multiplier (frequency_units, xargv[*xoptind] + 3) > 0)
{
ping_intv = 0;
}
else if (sscanf (xargv[*xoptind], "%lf%n", &ping_rate, &pos) == 1 && (xargv[*xoptind][pos] == 0 || strcmp (xargv[*xoptind] + pos, "Hz") == 0))
else if (sscanf (xargv[*xoptind], "%lf%n", &ping_rate, &pos) == 1 && (mult = lookup_multiplier (frequency_units, xargv[*xoptind] + pos)) > 0)
{
ping_rate *= mult;
if (ping_rate == 0) ping_intv = DDS_INFINITY;
else if (ping_rate > 0) ping_intv = (dds_duration_t) (1e9 / ping_rate + 0.5);
else error3 ("%s: invalid ping rate\n", xargv[*xoptind]);
}
else if (set_simple_uint32 (xoptind, xargc, xargv, "size", &baggagesize))
else if (set_simple_uint32 (xoptind, xargc, xargv, "size", size_units, &baggagesize))
{
/* no further work needed */
}
@ -1709,22 +1772,22 @@ static void set_mode_pub (int *xoptind, int xargc, char * const xargv[])
ping_frac = 0;
while (*xoptind < xargc && exact_string_int_map_lookup (modestrings, "mode string", xargv[*xoptind], false) == -1)
{
int pos = 0;
int pos = 0, mult = 1;
double r;
if (strcmp (xargv[*xoptind], "inf") == 0 || strcmp (xargv[*xoptind], "infHz") == 0)
if (strncmp (xargv[*xoptind], "inf", 3) == 0 && lookup_multiplier (frequency_units, xargv[*xoptind] + 3) > 0)
{
pub_rate = HUGE_VAL;
}
else if (sscanf (xargv[*xoptind], "%lf%n", &r, &pos) == 1 && (xargv[*xoptind][pos] == 0 || strcmp (xargv[*xoptind] + pos, "Hz") == 0))
else if (sscanf (xargv[*xoptind], "%lf%n", &r, &pos) == 1 && (mult = lookup_multiplier (frequency_units, xargv[*xoptind] + pos)) > 0)
{
if (r < 0) error3 ("%s: invalid publish rate\n", xargv[*xoptind]);
pub_rate = r;
pub_rate = r * mult;
}
else if (set_simple_uint32 (xoptind, xargc, xargv, "burst", &burstsize))
else if (set_simple_uint32 (xoptind, xargc, xargv, "burst", NULL, &burstsize))
{
/* no further work needed */
}
else if (set_simple_uint32 (xoptind, xargc, xargv, "size", &baggagesize))
else if (set_simple_uint32 (xoptind, xargc, xargv, "size", size_units, &baggagesize))
{
/* no further work needed */
}
@ -1753,7 +1816,7 @@ static void set_mode (int xoptind, int xargc, char * const xargv[])
pub_rate = 0.0;
submode = SM_NONE;
pingpongmode = SM_LISTENER;
ping_intv = (xoptind == xargc) ? DDS_SECS (1) : DDS_INFINITY;
ping_intv = DDS_INFINITY;
ping_frac = 0;
while (xoptind < xargc && (code = exact_string_int_map_lookup (modestrings, "mode string", xargv[xoptind], true)) != -1)
{
@ -1793,8 +1856,6 @@ int main (int argc, char *argv[])
argv0 = argv[0];
if (argc == 2 && strcmp (argv[1], "help") == 0)
usage ();
while ((opt = getopt (argc, argv, "cd:D:n:k:uLK:T:M:N:R:h")) != EOF)
{
switch (opt)
@ -1829,7 +1890,18 @@ int main (int argc, char *argv[])
default: error3 ("-%c: unknown option\n", opt); break;
}
}
set_mode (optind, argc, argv);
if (optind == argc || (optind + 1 == argc && strcmp (argv[optind], "help") == 0))
usage ();
else if (optind + 1 == argc && strcmp (argv[optind], "sanity") == 0)
{
char * const sanity[] = { "ping", "1Hz" };
set_mode (0, 2, sanity);
}
else
{
set_mode (optind, argc, argv);
}
if (nkeyvals == 0)
nkeyvals = 1;

View file

@ -40,6 +40,8 @@ struct CPUStats
string hostname;
unsigned long pid;
double maxrss;
unsigned long vcsw;
unsigned long ivcsw;
boolean some_above;
sequence<CPUStatThread> cpu;
};