cache: refactor to use a continguous table and bsearch
ci/woodpecker/push/woodpecker Pipeline was successful
Details
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) operationpull/241/head
parent
c0fa7879b2
commit
464672404e
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_ {
|
||||||
|
|
Loading…
Reference in New Issue