Re: [PATCH bpf-next 3/6] selftest/bpf/benchs: enhance argp parsing

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Jan 27, 2023 at 10:14 AM Anton Protopopov <aspsk@xxxxxxxxxxxxx> 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.


>
> Signed-off-by: Anton Protopopov <aspsk@xxxxxxxxxxxxx>
> ---
>  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?


> +       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?

> +
> +       /* 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) {
> +
> +       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?

>  }
>
>  static void collect_measurements(long delta_ns);

[...]



[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux