forked from ariadne/pkgconf
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) operationbsdstubs-errno
parent
c0fa7879b2
commit
464672404e
|
@ -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)
|
||||
{
|
||||
pkgconf_pkg_t *pkg = node->data;
|
||||
pkg = bsearch(id, client->cache_table,
|
||||
client->cache_count, sizeof (void *),
|
||||
cache_member_cmp);
|
||||
|
||||
if (!strcmp(pkg->id, id))
|
||||
if (pkg != NULL)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
|
|
@ -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_ {
|
||||
|
|
Loading…
Reference in New Issue