From: Anton Protopopov <aspsk@isovalent.com>
To: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Cc: bpf@vger.kernel.org, Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
Martin KaFai Lau <martin.lau@linux.dev>,
John Fastabend <john.fastabend@gmail.com>
Subject: Re: [PATCH bpf-next 3/6] selftest/bpf/benchs: enhance argp parsing
Date: Tue, 31 Jan 2023 14:35:16 +0100 [thread overview]
Message-ID: <Y9kZFBJP2FPstZzM@lavr> (raw)
In-Reply-To: <CAEf4BzYk2sBHsPPF5-dx=jnuaob3WvnTFyWH6DtnwF3T_=JVkg@mail.gmail.com>
On 23/01/30 04:07, Andrii Nakryiko wrote:
> On Fri, Jan 27, 2023 at 10:14 AM Anton Protopopov <aspsk@isovalent.com> wrote:
> >
> > To parse command line the bench utility uses the argp_parse() function. This
> > function takes as an argument a parent 'struct argp' structure which defines
> > common command line options and an array of children 'struct argp' structures
> > which defines additional command line options for particular benchmarks. This
> > implementation doesn't allow benchmarks to share option names, e.g., if two
> > benchmarks want to use, say, the --option option, then only one of them will
> > succeed (the first one encountered in the array). This will be convenient if
> > we could use the same option names in different benchmarks (with the same
> > semantics, e.g., --nr_loops=N).
> >
> > Fix this by calling the argp_parse() function twice. The first call is needed
> > to find the benchmark name. Given the name, we can patch the list of argp
> > children to only include the correct list of option. This way the right parser
> > will be executed. (If there's no a specific list of arguments, then only one
> > call is enough.) As was mentioned above, arguments with same names should have
> > the same semantics (which means in this case "taking a parameter or not"), but
> > can have different description and will have different parsers.
> >
> > As we now can share option names, this also makes sense to share the option ids.
> > Previously every benchmark defined a separate enum, like
> >
> > enum {
> > ARG_SMTH = 9000,
> > ARG_SMTH_ELSE = 9001,
> > };
> >
> > These ids were later used to distinguish between command line options:
> >
> > static const struct argp_option opts[] = {
> > { "smth", ARG_SMTH, "SMTH", 0,
> > "Set number of smth to configure smth"},
> > { "smth_else", ARG_SMTH_ELSE, "SMTH_ELSE", 0,
> > "Set number of smth_else to configure smth else"},
> > ...
> >
> > Move arguments id definitions to bench.h such that they are visible to every
> > benchmark (and such that there's no need to grep if this number is defined
> > already or not).
>
> On the other hand, if each benchmark defines their own set of IDs and
> parser, that keeps benchmarks more self-contained. Is there a real
> need to centralize everything? I don't see much benefit, tbh.
>
> If we want to centralize, then we can just bypass all the child parser
> machinery and have one centralized list of arguments. But I think it's
> good to have each benchmark self-contained and independent from other
> benchmarks.
When I was adding a new bench, it looked simpler to just add IDs to a global
list than to grep for what ID is not defined yet. This doesn't matter much
though, I can switch back to local IDs.
> >
> > Signed-off-by: Anton Protopopov <aspsk@isovalent.com>
> > ---
> > tools/testing/selftests/bpf/bench.c | 98 +++++++++++++++++--
> > tools/testing/selftests/bpf/bench.h | 20 ++++
> > .../bpf/benchs/bench_bloom_filter_map.c | 6 --
> > .../selftests/bpf/benchs/bench_bpf_loop.c | 4 -
> > .../bpf/benchs/bench_local_storage.c | 5 -
> > .../bench_local_storage_rcu_tasks_trace.c | 6 --
> > .../selftests/bpf/benchs/bench_ringbufs.c | 8 --
> > .../selftests/bpf/benchs/bench_strncmp.c | 4 -
> > 8 files changed, 110 insertions(+), 41 deletions(-)
> >
>
> [...]
>
> > +struct argp *bench_name_to_argp(const char *bench_name)
> > +{
> > +
> > +#define _SCMP(NAME) (!strcmp(bench_name, NAME))
> > +
> > + if (_SCMP("bloom-lookup") ||
> > + _SCMP("bloom-update") ||
> > + _SCMP("bloom-false-positive") ||
> > + _SCMP("hashmap-without-bloom") ||
> > + _SCMP("hashmap-with-bloom"))
> > + return &bench_bloom_map_argp;
> > +
> > + if (_SCMP("rb-libbpf") ||
> > + _SCMP("rb-custom") ||
> > + _SCMP("pb-libbpf") ||
> > + _SCMP("pb-custom"))
> > + return &bench_ringbufs_argp;
> > +
> > + if (_SCMP("local-storage-cache-seq-get") ||
> > + _SCMP("local-storage-cache-int-get") ||
> > + _SCMP("local-storage-cache-hashmap-control"))
> > + return &bench_local_storage_argp;
> > +
> > + if (_SCMP("local-storage-tasks-trace"))
> > + return &bench_local_storage_rcu_tasks_trace_argp;
> > +
> > + if (_SCMP("strncmp-no-helper") ||
> > + _SCMP("strncmp-helper"))
> > + return &bench_strncmp_argp;
> > +
> > + if (_SCMP("bpf-loop"))
> > + return &bench_bpf_loop_argp;
> > +
> > + /* no extra arguments */
> > + if (_SCMP("count-global") ||
> > + _SCMP("count-local") ||
> > + _SCMP("rename-base") ||
> > + _SCMP("rename-kprobe") ||
> > + _SCMP("rename-kretprobe") ||
> > + _SCMP("rename-rawtp") ||
> > + _SCMP("rename-fentry") ||
> > + _SCMP("rename-fexit") ||
> > + _SCMP("trig-base") ||
> > + _SCMP("trig-tp") ||
> > + _SCMP("trig-rawtp") ||
> > + _SCMP("trig-kprobe") ||
> > + _SCMP("trig-fentry") ||
> > + _SCMP("trig-fentry-sleep") ||
> > + _SCMP("trig-fmodret") ||
> > + _SCMP("trig-uprobe-base") ||
> > + _SCMP("trig-uprobe-with-nop") ||
> > + _SCMP("trig-uretprobe-with-nop") ||
> > + _SCMP("trig-uprobe-without-nop") ||
> > + _SCMP("trig-uretprobe-without-nop") ||
> > + _SCMP("bpf-hashmap-full-update"))
> > + return NULL;
> > +
> > +#undef _SCMP
> > +
>
> it's not good to have to maintain a separate list of benchmark names
> here. Let's maybe extend struct bench to specify extra parser and use
> that to figure out if we need to run nested child parser?
Yes, right, so then I can do something like
struct argp *bench_name_to_argp(const char *bench_name)
{
int i;
for (i = 0; i < ARRAY_SIZE(benchs); i++)
if (!strcmp(bench_name, benchs[i]->name))
return benchs[i]->argp;
fprintf(stderr, "benchmark '%s' not found\n", bench_name);
exit(1);
}
>
> > + fprintf(stderr, "%s: bench %s is unknown\n", __func__, bench_name);
> > + exit(1);
> > +}
> > +
> > static void parse_cmdline_args(int argc, char **argv)
> > {
> > static const struct argp argp = {
> > @@ -367,12 +426,35 @@ static void parse_cmdline_args(int argc, char **argv)
> > .doc = argp_program_doc,
> > .children = bench_parsers,
> > };
> > + static struct argp *bench_argp;
>
> nit: do you need static?
No, thanks for noting.
>
> > +
> > + /* Parse args for the first time to get bench name */
> > if (argp_parse(&argp, argc, argv, 0, NULL, NULL))
> > exit(1);
> > - if (!env.list && !env.bench_name) {
Also, I will switch to a simpler mode here: just find the bench_name, then
construct correct `struct argp`, then call argp_parse() once.
> > +
> > + if (env.list)
> > + return;
> > +
> > + if (!env.bench_name) {
> > argp_help(&argp, stderr, ARGP_HELP_DOC, "bench");
> > exit(1);
> > }
> > +
> > + /* Now check if there are custom options available. If not, then
> > + * everything is done, if yes, then we need to patch bench_parsers
> > + * so that bench_parsers[0] points to the right 'struct argp', and
> > + * bench_parsers[1] terminates the list.
> > + */
> > + bench_argp = bench_name_to_argp(env.bench_name);
> > + if (bench_argp) {
> > + bench_parsers[0].argp = bench_argp;
> > + bench_parsers[0].header = env.bench_name;
> > + memset(&bench_parsers[1], 0, sizeof(bench_parsers[1]));
> > +
> > + pos_args = 0;
> > + if (argp_parse(&argp, argc, argv, 0, NULL, NULL))
> > + exit(1);
> > + }
>
> this also feels like a big hack, why can't you just construct a
> single-item array based on child parser, instead of overwriting global
> array?
Sure, thanks.
> > }
> >
> > static void collect_measurements(long delta_ns);
>
> [...]
next prev parent reply other threads:[~2023-01-31 13:35 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-01-27 18:14 [PATCH bpf-next 0/6] New benchmark for hashmap lookups Anton Protopopov
2023-01-27 18:14 ` [PATCH bpf-next 1/6] selftest/bpf/benchs: fix a typo in bpf_hashmap_full_update Anton Protopopov
2023-01-27 18:14 ` [PATCH bpf-next 2/6] selftest/bpf/benchs: make a function static " Anton Protopopov
2023-01-27 18:14 ` [PATCH bpf-next 3/6] selftest/bpf/benchs: enhance argp parsing Anton Protopopov
2023-01-31 0:07 ` Andrii Nakryiko
2023-01-31 13:35 ` Anton Protopopov [this message]
2023-01-27 18:14 ` [PATCH bpf-next 4/6] selftest/bpf/benchs: make quiet option common Anton Protopopov
2023-01-31 0:10 ` Andrii Nakryiko
2023-01-31 10:57 ` Anton Protopopov
2023-01-31 18:51 ` Andrii Nakryiko
2023-01-31 18:57 ` Anton Protopopov
2023-01-27 18:14 ` [PATCH bpf-next 5/6] selftest/bpf/benchs: print less if the quiet option is set Anton Protopopov
2023-01-27 18:14 ` [PATCH bpf-next 6/6] selftest/bpf/benchs: Add benchmark for hashmap lookups Anton Protopopov
2023-01-31 0:16 ` Andrii Nakryiko
2023-01-31 11:01 ` Anton Protopopov
2023-01-31 0:22 ` Martin KaFai Lau
2023-01-31 11:05 ` Anton Protopopov
2023-01-31 22:50 ` Martin KaFai Lau
2023-02-01 9:12 ` Anton Protopopov
2023-01-31 0:17 ` [PATCH bpf-next 0/6] New " Andrii Nakryiko
2023-01-31 10:47 ` Anton Protopopov
2023-01-31 18:48 ` Andrii Nakryiko
2023-01-31 19:18 ` Anton Protopopov
2023-02-01 0:02 ` Andrii Nakryiko
2023-02-01 9:41 ` Anton Protopopov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=Y9kZFBJP2FPstZzM@lavr \
--to=aspsk@isovalent.com \
--cc=andrii.nakryiko@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=john.fastabend@gmail.com \
--cc=martin.lau@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.