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:
parent
ca04ac48de
commit
40ba6b207f
4 changed files with 122 additions and 34 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue