From 464672404ed64337855fcd634b0173657202183f Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Sun, 26 Jun 2022 04:30:35 +0000 Subject: [PATCH] cache: refactor to use a continguous table and bsearch cache functions are the hottest part of the pkgconf code when profiled, by removing the linked list for lookups, we can turn lookups into an O(k) operation --- libpkgconf/cache.c | 109 +++++++++++++++++++++++++++++++++------- libpkgconf/libpkgconf.h | 6 +-- 2 files changed, 94 insertions(+), 21 deletions(-) diff --git a/libpkgconf/cache.c b/libpkgconf/cache.c index 259577d..16190b5 100644 --- a/libpkgconf/cache.c +++ b/libpkgconf/cache.c @@ -29,6 +29,46 @@ * be shared across threads. */ +static int +cache_member_cmp(const void *a, const void *b) +{ + const char *key = a; + const pkgconf_pkg_t *pkg = *(void **) b; + + return strcmp(key, pkg->id); +} + +static int +cache_member_sort_cmp(const void *a, const void *b) +{ + const pkgconf_pkg_t *pkgA = *(void **) a; + const pkgconf_pkg_t *pkgB = *(void **) b; + + if (pkgA == NULL) + return 1; + + if (pkgB == NULL) + return -1; + + return strcmp(pkgA->id, pkgB->id); +} + +static void +cache_dump(const pkgconf_client_t *client) +{ + size_t i; + + PKGCONF_TRACE(client, "dumping package cache contents"); + + for (i = 0; i < client->cache_count; i++) + { + const pkgconf_pkg_t *pkg = client->cache_table[i]; + + PKGCONF_TRACE(client, "%zu: %p(%s)", + i, pkg, pkg == NULL ? "NULL" : pkg->id); + } +} + /* * !doc * @@ -46,17 +86,16 @@ pkgconf_pkg_t * pkgconf_cache_lookup(pkgconf_client_t *client, const char *id) { - pkgconf_node_t *node; + pkgconf_pkg_t **pkg; - PKGCONF_FOREACH_LIST_ENTRY(client->pkg_cache.head, node) + pkg = bsearch(id, client->cache_table, + client->cache_count, sizeof (void *), + cache_member_cmp); + + if (pkg != NULL) { - pkgconf_pkg_t *pkg = node->data; - - if (!strcmp(pkg->id, id)) - { - PKGCONF_TRACE(client, "found: %s @%p", id, pkg); - return pkgconf_pkg_ref(client, pkg); - } + PKGCONF_TRACE(client, "found: %s @%p", id, *pkg); + return pkgconf_pkg_ref(client, *pkg); } PKGCONF_TRACE(client, "miss: %s", id); @@ -82,12 +121,19 @@ pkgconf_cache_add(pkgconf_client_t *client, pkgconf_pkg_t *pkg) return; pkgconf_pkg_ref(client, pkg); - pkgconf_node_insert(&pkg->cache_iter, pkg, &client->pkg_cache); PKGCONF_TRACE(client, "added @%p to cache", pkg); /* mark package as cached */ pkg->flags |= PKGCONF_PKG_PROPF_CACHED; + + ++client->cache_count; + client->cache_table = reallocarray(client->cache_table, + client->cache_count, sizeof (void *)); + client->cache_table[client->cache_count - 1] = pkg; + + qsort(client->cache_table, client->cache_count, + sizeof(void *), cache_member_sort_cmp); } /* @@ -112,7 +158,31 @@ pkgconf_cache_remove(pkgconf_client_t *client, pkgconf_pkg_t *pkg) PKGCONF_TRACE(client, "removed @%p from cache", pkg); - pkgconf_node_delete(&pkg->cache_iter, &client->pkg_cache); + pkgconf_pkg_t **slot; + + slot = bsearch(pkg->id, client->cache_table, + client->cache_count, sizeof (void *), + cache_member_cmp); + + if (slot == NULL) + return; + + *slot = NULL; + + qsort(client->cache_table, client->cache_count, + sizeof(void *), cache_member_sort_cmp); + + if (client->cache_table[client->cache_count - 1] != NULL) + { + PKGCONF_TRACE(client, "end of cache table refers to %p, not NULL", + client->cache_table[client->cache_count - 1]); + cache_dump(client); + abort(); + } + + client->cache_count--; + client->cache_table = reallocarray(client->cache_table, + client->cache_count, sizeof(void *)); } static inline void @@ -141,12 +211,17 @@ clear_dependency_matches(pkgconf_list_t *list) void pkgconf_cache_free(pkgconf_client_t *client) { - pkgconf_node_t *iter, *iter2; + pkgconf_pkg_t **cache_table; + size_t i, count; + + cache_table = reallocarray(NULL, client->cache_count, sizeof (void *)); + memcpy(cache_table, client->cache_table, + client->cache_count * sizeof (void *)); /* first we clear cached match pointers */ - PKGCONF_FOREACH_LIST_ENTRY(client->pkg_cache.head, iter) + for (i = 0, count = client->cache_count; i < count; i++) { - pkgconf_pkg_t *pkg = iter->data; + pkgconf_pkg_t *pkg = cache_table[i]; clear_dependency_matches(&pkg->required); clear_dependency_matches(&pkg->requires_private); @@ -155,13 +230,11 @@ pkgconf_cache_free(pkgconf_client_t *client) } /* now forcibly free everything */ - PKGCONF_FOREACH_LIST_ENTRY_SAFE(client->pkg_cache.head, iter2, iter) + for (i = 0, count = client->cache_count; i < count; i++) { - pkgconf_pkg_t *pkg = iter->data; + pkgconf_pkg_t *pkg = cache_table[i]; pkgconf_pkg_free(client, pkg); } - memset(&client->pkg_cache, 0, sizeof client->pkg_cache); - PKGCONF_TRACE(client, "cleared package cache"); } diff --git a/libpkgconf/libpkgconf.h b/libpkgconf/libpkgconf.h index 8036946..27c5597 100644 --- a/libpkgconf/libpkgconf.h +++ b/libpkgconf/libpkgconf.h @@ -128,8 +128,6 @@ struct pkgconf_path_ { #define PKGCONF_PKG_PROPF_VIRTUAL 0x10 struct pkgconf_pkg_ { - pkgconf_node_t cache_iter; - int refcount; char *id; char *filename; @@ -171,7 +169,6 @@ typedef bool (*pkgconf_error_handler_func_t)(const char *msg, const pkgconf_clie struct pkgconf_client_ { pkgconf_list_t dir_list; - pkgconf_list_t pkg_cache; pkgconf_list_t filter_libdirs; pkgconf_list_t filter_includedirs; @@ -198,6 +195,9 @@ struct pkgconf_client_ { bool already_sent_notice; uint64_t serial; + + pkgconf_pkg_t **cache_table; + size_t cache_count; }; struct pkgconf_cross_personality_ {