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 the address or the interface name). Proper use of multiple network interfaces simultaneously will
come, but is not there yet. come, but is not there yet.
* ``AllowMulticast`` configures the circumstances under which multicast will be used. If the * ``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 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 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 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 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 split (which needs to be "a bit" less). Large values such as these typically improve performance
over the (current) default values. 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 has buffered too much unacknowledged data. There is some auto-tuning, the (current) default value
is a bit small to get really high throughput. is a bit small to get really high throughput.
The configurator tool ``cycloneddsconf`` can help in discovering the settings, as can the config The configurator tool ``cycloneddsconf`` can help in discovering the settings, as can the config
dump. Background information on configuring Cyclone DDS can be dump. Background information on configuring Cyclone DDS can be
found [here](https://docs/manual/config.rst). found [here](docs/manual/config.rst).
# Trademarks # 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); int n = (int) strlen (s->hostname);
if (n > 100) n = 100; 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) if (s->maxrss > 1048576)
pos += (size_t) snprintf (line + pos, sizeof (line) - pos, " rss:%.1fMB", s->maxrss / 1048576.0); 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 { else {
/* non-sensical value -- presumably maxrss is not available */ /* 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; const size_t init_pos = pos;
for (uint32_t i = 0; i < s->cpu._length; i++) for (uint32_t i = 0; i < s->cpu._length; i++)
{ {
@ -81,6 +82,8 @@ struct record_cputime_state_thr {
struct record_cputime_state { struct record_cputime_state {
bool supported; bool supported;
dds_time_t tprev; dds_time_t tprev;
uint32_t vcswprev;
uint32_t ivcswprev;
size_t nthreads; size_t nthreads;
struct record_cputime_state_thr *threads; struct record_cputime_state_thr *threads;
dds_entity_t wr; dds_entity_t wr;
@ -118,13 +121,20 @@ bool record_cputime (struct record_cputime_state *state, const char *prefix, dds
ddsrt_rusage_t usage; ddsrt_rusage_t usage;
if (ddsrt_getrusage (DDSRT_RUSAGE_SELF, &usage) < 0) if (ddsrt_getrusage (DDSRT_RUSAGE_SELF, &usage) < 0)
{
usage.maxrss = 0; usage.maxrss = 0;
usage.nvcsw = usage.nivcsw = 0;
}
double max = 0; double max = 0;
double du_skip = 0.0, ds_skip = 0.0; double du_skip = 0.0, ds_skip = 0.0;
const double dt = (double) (tnow - state->tprev) / 1e9; const double dt = (double) (tnow - state->tprev) / 1e9;
bool some_above = false; bool some_above = false;
state->s.maxrss = (double) usage.maxrss; 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; state->s.cpu._length = 0;
for (size_t i = 0; i < state->nthreads; i++) 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)); 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->tprev = dds_time ();
state->wr = wr; 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->threads = malloc ((size_t) n * sizeof (*state->threads));
state->nthreads = 0; state->nthreads = 0;
for (int32_t i = 0; i < n; i++) for (int32_t i = 0; i < n; i++)
{ {
struct record_cputime_state_thr * const thr = &state->threads[state->nthreads]; struct record_cputime_state_thr * const thr = &state->threads[state->nthreads];
ddsrt_rusage_t usage;
if (ddsrt_getrusage_anythread (tids[i], &usage) < 0) if (ddsrt_getrusage_anythread (tids[i], &usage) < 0)
continue; continue;
thr->tid = tids[i]; thr->tid = tids[i];

View file

@ -1524,29 +1524,36 @@ static void sigxfsz_handler (int sig __attribute__ ((unused)))
static void usage (void) static void usage (void)
{ {
printf ("\ printf ("\
%s help\n\ %s help (this text)\n\
%s sanity (ping 1Hz)\n\
%s [OPTIONS] MODE...\n\ %s [OPTIONS] MODE...\n\
\n\ \n\
OPTIONS:\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\ -T KS|K32|K256|OU topic (KS is default):\n\
KS seq num, key value, sequence-of-octets\n\ KS seq num, key value, sequence-of-octets\n\
K32 seq num, key value, array of 24 octets\n\ K32 seq num, key value, array of 24 octets\n\
K256 seq num, key value, array of 248 octets\n\ K256 seq num, key value, array of 248 octets\n\
OU seq num\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\ -u best-effort instead of reliable\n\
-k all|N keep-all or keep-last-N for data (ping/pong is\n\ -k all|N keep-all or keep-last-N for data (ping/pong is\n\
always keep-last-1)\n\ always keep-last-1)\n\
-n N number of key values to use for data (only for\n\ -c subscribe to CPU stats from peers and show them\n\
topics with a key value)\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\ -D DUR run for at most DUR seconds\n\
-N COUNT require at least COUNT matching participants\n\ -N COUNT require at least COUNT matching participants\n\
-M DUR require those participants to match within DUR seconds\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\ \n\
MODE... is zero or more of:\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\ 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\ a listener (default, unless -L has been specified) or a waitset.\n\
pong [waitset|listener]\n\ pong [waitset|listener]\n\
A \"dummy\" mode that serves two purposes: configuring the triggering.\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\ sub [waitset|listener|polling]\n\
Subscribe to data, with calls to take occurring either in a listener\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\ (default), when a waitset is triggered, or by polling at 1kHz.\n\
pub [R[Hz]] [size N] [burst N] [[ping] X%%]\n\ pub [R[Hz]] [size S] [burst N] [[ping] X%%]\n\
Publish bursts of data at rate R, optionally suffixed with Hz. If\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\ 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\ 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\ 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, for this, specify a percentage either as \"ping X%%\" (the\n\
\"ping\" keyword is optional, the %% sign is not).\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\ the last one given determines it for all) and should be either 0 (minimal,\n\
equivalent to 12) or >= 12.\n\ equivalent to 12) or >= 12.\n\
\n\ \n\
If no MODE specified, it defaults to a 1Hz ping + responding to any pings.\n\ EXIT STATUS:\n\
", argv0, argv0); \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); fflush (stdout);
exit (3); 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; 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) if (strcmp (xargv[*xoptind], token) != 0)
return false; return false;
else else
{ {
unsigned x; unsigned x;
int pos; int pos, mult;
if (++(*xoptind) == xargc) if (++(*xoptind) == xargc)
error3 ("argument missing in %s specification\n", token); error3 ("argument missing in %s specification\n", token);
if (sscanf (xargv[*xoptind], "%u%n", &x, &pos) == 1 && xargv[*xoptind][pos] == 0) if (sscanf (xargv[*xoptind], "%u%n", &x, &pos) == 1 && (mult = lookup_multiplier (units, xargv[*xoptind] + pos)) > 0)
*val = x; *val = x * (unsigned) mult;
else else
error3 ("%s: invalid %s specification\n", xargv[*xoptind], token); error3 ("%s: invalid %s specification\n", xargv[*xoptind], token);
return true; return true;
@ -1652,19 +1714,20 @@ static void set_mode_ping (int *xoptind, int xargc, char * const xargv[])
pingpongmode = SM_LISTENER; pingpongmode = SM_LISTENER;
while (*xoptind < xargc && exact_string_int_map_lookup (modestrings, "mode string", xargv[*xoptind], false) == -1) 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; 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; 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; if (ping_rate == 0) ping_intv = DDS_INFINITY;
else if (ping_rate > 0) ping_intv = (dds_duration_t) (1e9 / ping_rate + 0.5); 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 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 */ /* no further work needed */
} }
@ -1709,22 +1772,22 @@ static void set_mode_pub (int *xoptind, int xargc, char * const xargv[])
ping_frac = 0; ping_frac = 0;
while (*xoptind < xargc && exact_string_int_map_lookup (modestrings, "mode string", xargv[*xoptind], false) == -1) 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; 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; 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]); 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 */ /* 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 */ /* no further work needed */
} }
@ -1753,7 +1816,7 @@ static void set_mode (int xoptind, int xargc, char * const xargv[])
pub_rate = 0.0; pub_rate = 0.0;
submode = SM_NONE; submode = SM_NONE;
pingpongmode = SM_LISTENER; pingpongmode = SM_LISTENER;
ping_intv = (xoptind == xargc) ? DDS_SECS (1) : DDS_INFINITY; ping_intv = DDS_INFINITY;
ping_frac = 0; ping_frac = 0;
while (xoptind < xargc && (code = exact_string_int_map_lookup (modestrings, "mode string", xargv[xoptind], true)) != -1) 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]; 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) while ((opt = getopt (argc, argv, "cd:D:n:k:uLK:T:M:N:R:h")) != EOF)
{ {
switch (opt) switch (opt)
@ -1829,7 +1890,18 @@ int main (int argc, char *argv[])
default: error3 ("-%c: unknown option\n", opt); break; 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) if (nkeyvals == 0)
nkeyvals = 1; nkeyvals = 1;

View file

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