// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
#include <argp.h>
#include <linux/btf.h>
#include "local_storage_bench.skel.h"
#include "bench.h"
#include <test_btf.h>
static struct {
__u32 nr_maps;
__u32 hashmap_nr_keys_used;
} args = {
.nr_maps = 1000 ,
.hashmap_nr_keys_used = 1000 ,
};
enum {
ARG_NR_MAPS = 6000 ,
ARG_HASHMAP_NR_KEYS_USED = 6001 ,
};
static const struct argp_option opts[] = {
{ "nr_maps" , ARG_NR_MAPS, "NR_MAPS" , 0 ,
"Set number of local_storage maps" },
{ "hashmap_nr_keys_used" , ARG_HASHMAP_NR_KEYS_USED, "NR_KEYS" ,
0 , "When doing hashmap test, set number of hashmap keys test uses" },
{},
};
static error_t parse_arg(int key, char *arg, struct argp_state *state)
{
long ret;
switch (key) {
case ARG_NR_MAPS:
ret = strtol(arg, NULL, 10 );
if (ret < 1 || ret > UINT_MAX) {
fprintf(stderr, "invalid nr_maps" );
argp_usage(state);
}
args.nr_maps = ret;
break ;
case ARG_HASHMAP_NR_KEYS_USED:
ret = strtol(arg, NULL, 10 );
if (ret < 1 || ret > UINT_MAX) {
fprintf(stderr, "invalid hashmap_nr_keys_used" );
argp_usage(state);
}
args.hashmap_nr_keys_used = ret;
break ;
default :
return ARGP_ERR_UNKNOWN;
}
return 0 ;
}
const struct argp bench_local_storage_argp = {
.options = opts,
.parser = parse_arg,
};
/* Keep in sync w/ array of maps in bpf */
#define MAX_NR_MAPS 1000
/* keep in sync w/ same define in bpf */
#define HASHMAP_SZ 4194304
static void validate(void )
{
if (env.producer_cnt != 1 ) {
fprintf(stderr, "benchmark doesn't support multi-producer!\n" );
exit (1 );
}
if (env.consumer_cnt != 0 ) {
fprintf(stderr, "benchmark doesn't support consumer!\n" );
exit (1 );
}
if (args.nr_maps > MAX_NR_MAPS) {
fprintf(stderr, "nr_maps must be <= 1000\n" );
exit (1 );
}
if (args.hashmap_nr_keys_used > HASHMAP_SZ) {
fprintf(stderr, "hashmap_nr_keys_used must be <= %u\n" , HASHMAP_SZ);
exit (1 );
}
}
static struct {
struct local_storage_bench *skel;
void *bpf_obj;
struct bpf_map *array_of_maps;
} ctx;
static void prepopulate_hashmap(int fd)
{
int i, key, val;
/* local_storage gets will have BPF_LOCAL_STORAGE_GET_F_CREATE flag set, so
* populate the hashmap for a similar comparison
*/
for (i = 0 ; i < HASHMAP_SZ; i++) {
key = val = i;
if (bpf_map_update_elem(fd, &key, &val, 0 )) {
fprintf(stderr, "Error prepopulating hashmap (key %d)\n" , key);
exit (1 );
}
}
}
static void __setup(struct bpf_program *prog, bool hashmap)
{
struct bpf_map *inner_map;
int i, fd, mim_fd, err;
LIBBPF_OPTS(bpf_map_create_opts, create_opts);
if (!hashmap)
create_opts.map_flags = BPF_F_NO_PREALLOC;
ctx.skel->rodata->num_maps = args.nr_maps;
ctx.skel->rodata->hashmap_num_keys = args.hashmap_nr_keys_used;
inner_map = bpf_map__inner_map(ctx.array_of_maps);
create_opts.btf_key_type_id = bpf_map__btf_key_type_id(inner_map);
create_opts.btf_value_type_id = bpf_map__btf_value_type_id(inner_map);
err = local_storage_bench__load(ctx.skel);
if (err) {
fprintf(stderr, "Error loading skeleton\n" );
goto err_out;
}
create_opts.btf_fd = bpf_object__btf_fd(ctx.skel->obj);
mim_fd = bpf_map__fd(ctx.array_of_maps);
if (mim_fd < 0 ) {
fprintf(stderr, "Error getting map_in_map fd\n" );
goto err_out;
}
for (i = 0 ; i < args.nr_maps; i++) {
if (hashmap)
fd = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, sizeof (int ),
sizeof (int ), HASHMAP_SZ, &create_opts);
else
fd = bpf_map_create(BPF_MAP_TYPE_TASK_STORAGE, NULL, sizeof (int ),
sizeof (int ), 0 , &create_opts);
if (fd < 0 ) {
fprintf(stderr, "Error creating map %d: %d\n" , i, fd);
goto err_out;
}
if (hashmap)
prepopulate_hashmap(fd);
err = bpf_map_update_elem(mim_fd, &i, &fd, 0 );
if (err) {
fprintf(stderr, "Error updating array-of-maps w/ map %d\n" , i);
goto err_out;
}
}
if (!bpf_program__attach(prog)) {
fprintf(stderr, "Error attaching bpf program\n" );
goto err_out;
}
return ;
err_out:
exit (1 );
}
static void hashmap_setup(void )
{
struct local_storage_bench *skel;
setup_libbpf();
skel = local_storage_bench__open();
ctx.skel = skel;
ctx.array_of_maps = skel->maps.array_of_hash_maps;
skel->rodata->use_hashmap = 1 ;
skel->rodata->interleave = 0 ;
__setup(skel->progs.get_local, true );
}
static void local_storage_cache_get_setup(void )
{
struct local_storage_bench *skel;
setup_libbpf();
skel = local_storage_bench__open();
ctx.skel = skel;
ctx.array_of_maps = skel->maps.array_of_local_storage_maps;
skel->rodata->use_hashmap = 0 ;
skel->rodata->interleave = 0 ;
__setup(skel->progs.get_local, false );
}
static void local_storage_cache_get_interleaved_setup(void )
{
struct local_storage_bench *skel;
setup_libbpf();
skel = local_storage_bench__open();
ctx.skel = skel;
ctx.array_of_maps = skel->maps.array_of_local_storage_maps;
skel->rodata->use_hashmap = 0 ;
skel->rodata->interleave = 1 ;
__setup(skel->progs.get_local, false );
}
static void measure(struct bench_res *res)
{
res->hits = atomic_swap(&ctx.skel->bss->hits, 0 );
res->important_hits = atomic_swap(&ctx.skel->bss->important_hits, 0 );
}
static inline void trigger_bpf_program(void )
{
syscall(__NR_getpgid);
}
static void *producer(void *input)
{
while (true )
trigger_bpf_program();
return NULL;
}
/* cache sequential and interleaved get benchs test local_storage get
* performance , specifically they demonstrate performance cliff of
* current list - plus - cache local_storage model .
*
* cache sequential get : call bpf_task_storage_get on n maps in order
* cache interleaved get : like " sequential get " , but interleave 4 calls to the
* ' important ' map ( idx 0 in array_of_maps ) for every 10 calls . Goal
* is to mimic environment where many progs are accessing their local_storage
* maps , with ' our ' prog needing to access its map more often than others
*/
const struct bench bench_local_storage_cache_seq_get = {
.name = "local-storage-cache-seq-get" ,
.argp = &bench_local_storage_argp,
.validate = validate,
.setup = local_storage_cache_get_setup,
.producer_thread = producer,
.measure = measure,
.report_progress = local_storage_report_progress,
.report_final = local_storage_report_final,
};
const struct bench bench_local_storage_cache_interleaved_get = {
.name = "local-storage-cache-int-get" ,
.argp = &bench_local_storage_argp,
.validate = validate,
.setup = local_storage_cache_get_interleaved_setup,
.producer_thread = producer,
.measure = measure,
.report_progress = local_storage_report_progress,
.report_final = local_storage_report_final,
};
const struct bench bench_local_storage_cache_hashmap_control = {
.name = "local-storage-cache-hashmap-control" ,
.argp = &bench_local_storage_argp,
.validate = validate,
.setup = hashmap_setup,
.producer_thread = producer,
.measure = measure,
.report_progress = local_storage_report_progress,
.report_final = local_storage_report_final,
};
Messung V0.5 in Prozent C=97 H=84 G=90
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-10-12)
¤
*© Formatika GbR, Deutschland