summaryrefslogtreecommitdiff
path: root/res/res_sorcery_config.c
diff options
context:
space:
mode:
authorJoshua Colp <jcolp@digium.com>2015-02-15 17:43:21 +0000
committerJoshua Colp <jcolp@digium.com>2015-02-15 17:43:21 +0000
commite78dd398857924e7617b388262d5ff37a0bf6232 (patch)
tree6791c2c0d19d7e88a9e66018ee122431062fb42b /res/res_sorcery_config.c
parente6fe69b76c1f01b2c1fe3ce0b534494e60aa819b (diff)
res_sorcery_config: Improve object lookup times.
The res_sorcery_config module currently uses a fixed bucket size of 53. This means that depending on the number of objects you either end up with excess buckets or a lot of collisions. Due to the way that res_sorcery_config is implemented it's actually possible to make the bucket size dynamic based on the number of objects. This is due to the fact that each loading of the config file produces a new container and does not modify the existing one. This change uses the number of expected objects and finds a prime number near it. In practice depending on the number of objects this can speed up lookups anywhere from 2X to 15X. This change also removes the lock from the container as it is not needed. Review: https://reviewboard.asterisk.org/r/4423/ ........ Merged revisions 431841 from http://svn.asterisk.org/svn/asterisk/branches/13 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@431842 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'res/res_sorcery_config.c')
-rw-r--r--res/res_sorcery_config.c46
1 files changed, 37 insertions, 9 deletions
diff --git a/res/res_sorcery_config.c b/res/res_sorcery_config.c
index 1680196aa..f8ea864ff 100644
--- a/res/res_sorcery_config.c
+++ b/res/res_sorcery_config.c
@@ -39,9 +39,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/astobj2.h"
#include "asterisk/config.h"
#include "asterisk/uuid.h"
-
-/*! \brief Default number of buckets for sorcery objects */
-#define DEFAULT_OBJECT_BUCKETS 53
+#include "asterisk/hashtab.h"
/*! \brief Structure for storing configuration file sourced objects */
struct sorcery_config {
@@ -183,7 +181,7 @@ static void *sorcery_config_retrieve_id(const struct ast_sorcery *sorcery, void
struct sorcery_config *config = data;
RAII_VAR(struct ao2_container *, objects, ao2_global_obj_ref(config->objects), ao2_cleanup);
- return objects ? ao2_find(objects, id, OBJ_KEY | OBJ_NOLOCK) : NULL;
+ return objects ? ao2_find(objects, id, OBJ_KEY) : NULL;
}
static void sorcery_config_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const struct ast_variable *fields)
@@ -237,6 +235,7 @@ static void sorcery_config_internal_load(void *data, const struct ast_sorcery *s
struct ast_category *category = NULL;
RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup);
const char *id = NULL;
+ unsigned int buckets = 0;
if (!cfg) {
ast_log(LOG_ERROR, "Unable to load config file '%s'\n", config->filename);
@@ -249,7 +248,37 @@ static void sorcery_config_internal_load(void *data, const struct ast_sorcery *s
return;
}
- if (!(objects = ao2_container_alloc(config->buckets, sorcery_config_hash, sorcery_config_cmp))) {
+ if (!config->buckets) {
+ while ((category = ast_category_browse_filtered(cfg, NULL, category, NULL))) {
+
+ /* If given criteria has not been met skip the category, it is not applicable */
+ if (!sorcery_is_criteria_met(ast_category_first(category), config->criteria)) {
+ continue;
+ }
+
+ buckets++;
+ }
+
+ /* Determine the optimal number of buckets */
+ while (buckets && !ast_is_prime(buckets)) {
+ /* This purposely goes backwards to ensure that the container doesn't have a ton of
+ * empty buckets for objects that will never get added.
+ */
+ buckets--;
+ }
+
+ if (!buckets) {
+ buckets = 1;
+ }
+ } else {
+ buckets = config->buckets;
+ }
+
+ ast_debug(2, "Using bucket size of '%d' for objects of type '%s' from '%s'\n",
+ buckets, type, config->filename);
+
+ if (!(objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, buckets,
+ sorcery_config_hash, sorcery_config_cmp))) {
ast_log(LOG_ERROR, "Could not create bucket for new objects from '%s', keeping existing objects\n",
config->filename);
ast_config_destroy(cfg);
@@ -288,7 +317,7 @@ static void sorcery_config_internal_load(void *data, const struct ast_sorcery *s
ast_log(LOG_NOTICE, "Retaining existing configuration for object of type '%s' with id '%s'\n", type, id);
}
- ao2_link_flags(objects, obj, OBJ_NOLOCK);
+ ao2_link(objects, obj);
}
ao2_global_obj_replace_unref(config->objects, objects);
@@ -317,7 +346,6 @@ static void *sorcery_config_open(const char *data)
ast_uuid_generate_str(config->uuid, sizeof(config->uuid));
ast_rwlock_init(&config->objects.lock);
- config->buckets = DEFAULT_OBJECT_BUCKETS;
strcpy(config->filename, filename);
while ((option = strsep(&tmp, ","))) {
@@ -325,8 +353,8 @@ static void *sorcery_config_open(const char *data)
if (!strcasecmp(name, "buckets")) {
if (sscanf(value, "%30u", &config->buckets) != 1) {
- ast_log(LOG_ERROR, "Unsupported bucket size of '%s' used for configuration file '%s', defaulting to '%d'\n",
- value, filename, DEFAULT_OBJECT_BUCKETS);
+ ast_log(LOG_ERROR, "Unsupported bucket size of '%s' used for configuration file '%s', defaulting to automatic determination\n",
+ value, filename);
}
} else if (!strcasecmp(name, "integrity")) {
if (!strcasecmp(value, "file")) {