cache: refactor to use a continguous table and bsearch
ci/woodpecker/push/woodpecker Pipeline was successful Details

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
pull/241/head
Ariadne Conill 2022-06-26 04:30:35 +00:00
parent c0fa7879b2
commit 464672404e
2 changed files with 94 additions and 21 deletions

View File

@ -29,6 +29,46 @@
* be shared across threads. * 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 * !doc
* *
@ -46,17 +86,16 @@
pkgconf_pkg_t * pkgconf_pkg_t *
pkgconf_cache_lookup(pkgconf_client_t *client, const char *id) 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; PKGCONF_TRACE(client, "found: %s @%p", id, *pkg);
return pkgconf_pkg_ref(client, *pkg);
if (!strcmp(pkg->id, id))
{
PKGCONF_TRACE(client, "found: %s @%p", id, pkg);
return pkgconf_pkg_ref(client, pkg);
}
} }
PKGCONF_TRACE(client, "miss: %s", id); PKGCONF_TRACE(client, "miss: %s", id);
@ -82,12 +121,19 @@ pkgconf_cache_add(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
return; return;
pkgconf_pkg_ref(client, pkg); pkgconf_pkg_ref(client, pkg);
pkgconf_node_insert(&pkg->cache_iter, pkg, &client->pkg_cache);
PKGCONF_TRACE(client, "added @%p to cache", pkg); PKGCONF_TRACE(client, "added @%p to cache", pkg);
/* mark package as cached */ /* mark package as cached */
pkg->flags |= PKGCONF_PKG_PROPF_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_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 static inline void
@ -141,12 +211,17 @@ clear_dependency_matches(pkgconf_list_t *list)
void void
pkgconf_cache_free(pkgconf_client_t *client) 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 */ /* 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->required);
clear_dependency_matches(&pkg->requires_private); clear_dependency_matches(&pkg->requires_private);
@ -155,13 +230,11 @@ pkgconf_cache_free(pkgconf_client_t *client)
} }
/* now forcibly free everything */ /* 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); pkgconf_pkg_free(client, pkg);
} }
memset(&client->pkg_cache, 0, sizeof client->pkg_cache);
PKGCONF_TRACE(client, "cleared package cache"); PKGCONF_TRACE(client, "cleared package cache");
} }

View File

@ -128,8 +128,6 @@ struct pkgconf_path_ {
#define PKGCONF_PKG_PROPF_VIRTUAL 0x10 #define PKGCONF_PKG_PROPF_VIRTUAL 0x10
struct pkgconf_pkg_ { struct pkgconf_pkg_ {
pkgconf_node_t cache_iter;
int refcount; int refcount;
char *id; char *id;
char *filename; char *filename;
@ -171,7 +169,6 @@ typedef bool (*pkgconf_error_handler_func_t)(const char *msg, const pkgconf_clie
struct pkgconf_client_ { struct pkgconf_client_ {
pkgconf_list_t dir_list; pkgconf_list_t dir_list;
pkgconf_list_t pkg_cache;
pkgconf_list_t filter_libdirs; pkgconf_list_t filter_libdirs;
pkgconf_list_t filter_includedirs; pkgconf_list_t filter_includedirs;
@ -198,6 +195,9 @@ struct pkgconf_client_ {
bool already_sent_notice; bool already_sent_notice;
uint64_t serial; uint64_t serial;
pkgconf_pkg_t **cache_table;
size_t cache_count;
}; };
struct pkgconf_cross_personality_ { struct pkgconf_cross_personality_ {