mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
perf maps: Switch from rbtree to lazily sorted array for addresses
Maps is a collection of maps primarily sorted by the starting address of the map. Prior to this change the maps were held in an rbtree requiring 4 pointers per node. Prior to reference count checking, the rbnode was embedded in the map so 3 pointers per node were necessary. This change switches the rbtree to an array lazily sorted by address, much as the array sorting nodes by name. 1 pointer is needed per node, but to avoid excessive resizing the backing array may be twice the number of used elements. Meaning the memory overhead is roughly half that of the rbtree. For a perf record with "--no-bpf-event -g -a" of true, the memory overhead of perf inject is reduce fom 3.3MB to 3MB, so 10% or 300KB is saved. Map inserts always happen at the end of the array. The code tracks whether the insertion violates the sorting property. O(log n) rb-tree complexity is switched to O(1). Remove slides the array, so O(log n) rb-tree complexity is degraded to O(n). A find may need to sort the array using qsort which is O(n*log n), but in general the maps should be sorted and so average performance should be O(log n) as with the rbtree. An rbtree node consumes a cache line, but with the array 4 nodes fit on a cache line. Iteration is simplified to scanning an array rather than pointer chasing. Overall it is expected the performance after the change should be comparable to before, but with half of the memory consumed. To avoid a list and repeated logic around splitting maps, maps__merge_in is rewritten in terms of maps__fixup_overlap_and_insert. maps_merge_in splits the given mapping inserting remaining gaps. maps__fixup_overlap_and_insert splits the existing mappings, then adds the incoming mapping. By adding the new mapping first, then re-inserting the existing mappings the splitting behavior matches. Signed-off-by: Ian Rogers <irogers@google.com> Acked-by: Namhyung Kim <namhyung@kernel.org> Cc: K Prateek Nayak <kprateek.nayak@amd.com> Cc: James Clark <james.clark@arm.com> Cc: Vincent Whitchurch <vincent.whitchurch@axis.com> Cc: Alexey Dobriyan <adobriyan@gmail.com> Cc: Colin Ian King <colin.i.king@gmail.com> Cc: Changbin Du <changbin.du@huawei.com> Cc: Masami Hiramatsu <mhiramat@kernel.org> Cc: Song Liu <song@kernel.org> Cc: Leo Yan <leo.yan@linux.dev> Cc: Athira Rajeev <atrajeev@linux.vnet.ibm.com> Cc: Liam Howlett <liam.howlett@oracle.com> Cc: Artem Savkov <asavkov@redhat.com> Cc: bpf@vger.kernel.org Signed-off-by: Namhyung Kim <namhyung@kernel.org> Link: https://lore.kernel.org/r/20240210031746.4057262-2-irogers@google.com
This commit is contained in:
parent
39d14c0dd6
commit
659ad3492b
@ -156,6 +156,9 @@ static int test__maps__merge_in(struct test_suite *t __maybe_unused, int subtest
|
|||||||
TEST_ASSERT_VAL("merge check failed", !ret);
|
TEST_ASSERT_VAL("merge check failed", !ret);
|
||||||
|
|
||||||
maps__zput(maps);
|
maps__zput(maps);
|
||||||
|
map__zput(map_kcore1);
|
||||||
|
map__zput(map_kcore2);
|
||||||
|
map__zput(map_kcore3);
|
||||||
return TEST_OK;
|
return TEST_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,6 +168,7 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
|
|||||||
if (dso == NULL)
|
if (dso == NULL)
|
||||||
goto out_delete;
|
goto out_delete;
|
||||||
|
|
||||||
|
assert(!dso->kernel);
|
||||||
map__init(result, start, start + len, pgoff, dso);
|
map__init(result, start, start + len, pgoff, dso);
|
||||||
|
|
||||||
if (anon || no_dso) {
|
if (anon || no_dso) {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -25,21 +25,56 @@ static inline struct map_list_node *map_list_node__new(void)
|
|||||||
return malloc(sizeof(struct map_list_node));
|
return malloc(sizeof(struct map_list_node));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct map *maps__find(struct maps *maps, u64 addr);
|
/*
|
||||||
|
* Locking/sorting note:
|
||||||
|
*
|
||||||
|
* Sorting is done with the write lock, iteration and binary searching happens
|
||||||
|
* under the read lock requiring being sorted. There is a race between sorting
|
||||||
|
* releasing the write lock and acquiring the read lock for iteration/searching
|
||||||
|
* where another thread could insert and break the sorting of the maps. In
|
||||||
|
* practice inserting maps should be rare meaning that the race shouldn't lead
|
||||||
|
* to live lock. Removal of maps doesn't break being sorted.
|
||||||
|
*/
|
||||||
|
|
||||||
DECLARE_RC_STRUCT(maps) {
|
DECLARE_RC_STRUCT(maps) {
|
||||||
struct rb_root entries;
|
|
||||||
struct rw_semaphore lock;
|
struct rw_semaphore lock;
|
||||||
struct machine *machine;
|
/**
|
||||||
struct map *last_search_by_name;
|
* @maps_by_address: array of maps sorted by their starting address if
|
||||||
|
* maps_by_address_sorted is true.
|
||||||
|
*/
|
||||||
|
struct map **maps_by_address;
|
||||||
|
/**
|
||||||
|
* @maps_by_name: optional array of maps sorted by their dso name if
|
||||||
|
* maps_by_name_sorted is true.
|
||||||
|
*/
|
||||||
struct map **maps_by_name;
|
struct map **maps_by_name;
|
||||||
refcount_t refcnt;
|
struct machine *machine;
|
||||||
unsigned int nr_maps;
|
|
||||||
unsigned int nr_maps_allocated;
|
|
||||||
#ifdef HAVE_LIBUNWIND_SUPPORT
|
#ifdef HAVE_LIBUNWIND_SUPPORT
|
||||||
void *addr_space;
|
void *addr_space;
|
||||||
const struct unwind_libunwind_ops *unwind_libunwind_ops;
|
const struct unwind_libunwind_ops *unwind_libunwind_ops;
|
||||||
#endif
|
#endif
|
||||||
|
refcount_t refcnt;
|
||||||
|
/**
|
||||||
|
* @nr_maps: number of maps_by_address, and possibly maps_by_name,
|
||||||
|
* entries that contain maps.
|
||||||
|
*/
|
||||||
|
unsigned int nr_maps;
|
||||||
|
/**
|
||||||
|
* @nr_maps_allocated: number of entries in maps_by_address and possibly
|
||||||
|
* maps_by_name.
|
||||||
|
*/
|
||||||
|
unsigned int nr_maps_allocated;
|
||||||
|
/**
|
||||||
|
* @last_search_by_name_idx: cache of last found by name entry's index
|
||||||
|
* as frequent searches for the same dso name are common.
|
||||||
|
*/
|
||||||
|
unsigned int last_search_by_name_idx;
|
||||||
|
/** @maps_by_address_sorted: is maps_by_address sorted. */
|
||||||
|
bool maps_by_address_sorted;
|
||||||
|
/** @maps_by_name_sorted: is maps_by_name sorted. */
|
||||||
|
bool maps_by_name_sorted;
|
||||||
|
/** @ends_broken: does the map contain a map where end values are unset/unsorted? */
|
||||||
|
bool ends_broken;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define KMAP_NAME_LEN 256
|
#define KMAP_NAME_LEN 256
|
||||||
@ -102,6 +137,7 @@ size_t maps__fprintf(struct maps *maps, FILE *fp);
|
|||||||
int maps__insert(struct maps *maps, struct map *map);
|
int maps__insert(struct maps *maps, struct map *map);
|
||||||
void maps__remove(struct maps *maps, struct map *map);
|
void maps__remove(struct maps *maps, struct map *map);
|
||||||
|
|
||||||
|
struct map *maps__find(struct maps *maps, u64 addr);
|
||||||
struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp);
|
struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp);
|
||||||
struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, struct map **mapp);
|
struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, struct map **mapp);
|
||||||
|
|
||||||
@ -117,8 +153,6 @@ struct map *maps__find_next_entry(struct maps *maps, struct map *map);
|
|||||||
|
|
||||||
int maps__merge_in(struct maps *kmaps, struct map *new_map);
|
int maps__merge_in(struct maps *kmaps, struct map *new_map);
|
||||||
|
|
||||||
void __maps__sort_by_name(struct maps *maps);
|
|
||||||
|
|
||||||
void maps__fixup_end(struct maps *maps);
|
void maps__fixup_end(struct maps *maps);
|
||||||
|
|
||||||
void maps__load_first(struct maps *maps);
|
void maps__load_first(struct maps *maps);
|
||||||
|
Loading…
Reference in New Issue
Block a user