Improved performance in get/set operation 56/106656/8 accepted/tizen/3.0/common/20170118.130815 accepted/tizen/3.0/ivi/20170118.042650 accepted/tizen/3.0/mobile/20170118.042557 accepted/tizen/3.0/tv/20170118.042614 accepted/tizen/3.0/wearable/20170118.042631 submit/tizen_3.0/20170116.091702
authorJiwoong Im <jiwoong.im@samsung.com>
Thu, 22 Dec 2016 08:33:08 +0000 (17:33 +0900)
committerJiwoong Im <jiwoong.im@samsung.com>
Wed, 11 Jan 2017 11:02:55 +0000 (20:02 +0900)
- Add hashtable in buxton daemon to reduce db access.
  Add hashtable in client for repeated get operation on the same key.
  Send notis to client after sending result of set operation.

- Brief profiling data
 +---------------------------------------------------+
 | - Gerenal set/get operation                       |
 | vconf set average : 5.420375 ms -> 3.430805 ms    |
 | vconf get average : 1.721345 ms -> 0.592360 ms    |
 |                                                   |
 | - Repeated get operation on the same key          |
 | vconf get average : 1.721345 ms -> 0.010990 ms    |
 +---------------------------------------------------+

Change-Id: Ib7ffd75dbd3bb11b76e9729f4177605848c47dfe
Signed-off-by: Jiwoong Im <jiwoong.im@samsung.com>
client/CMakeLists.txt
common/cache.c [new file with mode: 0644]
common/cache.h [new file with mode: 0644]
common/direct.c
daemon/CMakeLists.txt
daemon/daemon.c
lib/CMakeLists.txt
vconf-compat/vconf.c

index 81df99e..6e43afe 100644 (file)
@@ -11,6 +11,7 @@ SET(SRC c_main.c
        ../common/backends.c
        ../common/config.c
        ../common/serialize.c
+       ../common/cache.c
 )
 ADD_EXECUTABLE(${TARGET} ${SRC})
 SET_TARGET_PROPERTIES(${TARGET} PROPERTIES
diff --git a/common/cache.c b/common/cache.c
new file mode 100644 (file)
index 0000000..0dbc5ac
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "buxton2.h"
+#include "log.h"
+#include "cache.h"
+
+#define CACHE_COUNT_MAX 2000
+#define CACHE_SIZE_MAX (1024 * 1024) /* 1MB per cache (MAX) */
+
+#define DEFAULT_DROP_CACHE_SIZE 50
+
+#undef LOG_TAG
+#define LOG_TAG "BXT_CACHE"
+
+typedef struct _cache {
+       GHashTable *hash_table;
+       enum buxton_layer_type layer_type;
+       int count;
+       int size;
+} bxt_cache;
+
+typedef struct _cache_data {
+       enum buxton_layer_type layer_type;
+       int len;        /* data size */
+       void *data;     /* raw data */
+} cache_data;
+
+static bxt_cache *g_base_db_cache;
+static bxt_cache *g_normal_db_cache;
+
+static bxt_cache *get_cache(enum buxton_layer_type layer_type)
+{
+       return (layer_type == BUXTON_LAYER_NORMAL) ?
+               g_normal_db_cache : g_base_db_cache;
+}
+
+static void destroy_key(gpointer key)
+{
+       if (key)
+               free(key);
+}
+
+static void destroy_data(gpointer data)
+{
+       cache_data *cdata = (cache_data *) data;
+       bxt_cache *cache = NULL;
+
+       if (!cdata)
+               return;
+
+       cache = get_cache(cdata->layer_type);
+
+       if (!cache)
+               return;
+
+       if (cdata->data) {
+               if (cache) {
+                       cache->size -= cdata->len;
+                       cache->count--;
+               }
+               free(cdata->data);
+       }
+
+       free(data);
+
+}
+
+void cache_init(void)
+{
+       g_base_db_cache = (bxt_cache *)malloc(sizeof(bxt_cache));
+       if (!g_base_db_cache)
+               return;
+       g_normal_db_cache = (bxt_cache *)malloc(sizeof(bxt_cache));
+       if (!g_normal_db_cache) {
+               free(g_base_db_cache);
+               g_base_db_cache = NULL;
+               return;
+       }
+
+       g_base_db_cache->hash_table = g_hash_table_new_full(g_str_hash,
+                       g_str_equal,
+                       (GDestroyNotify)destroy_key,
+                       (GDestroyNotify)destroy_data);
+       g_base_db_cache->size = 0;
+       g_base_db_cache->count = 0;
+
+       g_normal_db_cache->hash_table = g_hash_table_new_full(g_str_hash,
+                       g_str_equal,
+                       (GDestroyNotify)destroy_key,
+                       (GDestroyNotify)destroy_data);
+       g_normal_db_cache->size = 0;
+       g_normal_db_cache->count = 0;
+}
+
+void cache_remove(enum buxton_layer_type layer_type, const char *key)
+{
+       GHashTable *hash_table = NULL;
+
+       bxt_cache *cache = get_cache(layer_type);
+
+       if (!cache)
+               return;
+
+       hash_table = cache->hash_table;
+
+       if (!hash_table)
+               return;
+
+       if (g_hash_table_remove(hash_table, key)) {
+               bxt_dbg("[%s cache] removed (%s)/count(%d)/total size(%d)",
+                               (layer_type == BUXTON_LAYER_NORMAL) ?
+                               "NORMAL" : "BASE",
+                               key, cache->count, cache->size);
+       }
+}
+
+void cache_update_data(enum buxton_layer_type layer_type,
+               const char *key, uint8_t *data, int len)
+{
+       GHashTable *hash_table = NULL;
+       gpointer raw_data;
+
+       cache_data *cdata;
+       bxt_cache *cache = get_cache(layer_type);
+
+       if (!cache)
+               return;
+
+       hash_table = cache->hash_table;
+
+       raw_data =  g_hash_table_lookup(hash_table, key);
+
+       if (!raw_data) {
+               cache_insert(layer_type, key, data, len);
+               return;
+       }
+
+       cdata = (cache_data *)raw_data;
+
+       if (cdata->data)
+               free(cdata->data);
+
+       cdata->data = malloc(len);
+       if (!cdata->data)
+               return;
+
+       memcpy(cdata->data, data, len);
+
+       cdata->len = len;
+}
+
+static void cache_drop(enum buxton_layer_type layer_type, int size)
+{
+       GHashTableIter iter;
+       gpointer key;
+       gpointer value;
+       int remain = size;
+       GHashTable *hash_table = NULL;
+
+       bxt_cache *cache = get_cache(layer_type);
+
+       if (!cache)
+               return;
+
+       hash_table = cache->hash_table;
+
+       g_hash_table_iter_init(&iter, hash_table);
+
+       while (g_hash_table_iter_next(&iter, &key, &value)) {
+               if (remain > 0) {
+                       bxt_dbg("[%s cache][%d] removed (%s)",
+                                       (layer_type == BUXTON_LAYER_NORMAL) ?
+                                       "NORMAL" : "BASE", remain, (char *)key);
+                       g_hash_table_iter_remove(&iter);
+                       remain--;
+               }
+       }
+}
+
+void cache_insert(enum buxton_layer_type layer_type, const char *key,
+               uint8_t *data, int len)
+{
+       GHashTable *hash_table = NULL;
+       char *key_name;
+
+       bxt_cache *cache = get_cache(layer_type);
+       cache_data *raw_data;
+
+       if (!cache)
+               return;
+
+       hash_table = cache->hash_table;
+
+       if (g_hash_table_contains(hash_table, key))
+               return;
+
+       if ((g_hash_table_size(hash_table) >= CACHE_COUNT_MAX) ||
+                       (cache->size >= CACHE_SIZE_MAX))
+               cache_drop(layer_type, DEFAULT_DROP_CACHE_SIZE);
+
+       key_name = (char *)strdup(key);
+       if (!key_name)
+               return;
+
+       raw_data = (cache_data *)malloc(sizeof(cache_data));
+       if (!raw_data) {
+               free(key_name);
+               return;
+       }
+
+       raw_data->data = malloc(len);
+       if (!raw_data->data) {
+               free(key_name);
+               free(raw_data);
+               return;
+       }
+
+       memcpy(raw_data->data, data, len);
+
+       raw_data->len = len;
+       raw_data->layer_type = layer_type;
+
+       g_hash_table_insert(hash_table, key_name, (gpointer) raw_data);
+
+       cache->count++;
+       cache->size += len;
+
+       bxt_dbg("[%s cache] inserted (%s)/size(%d)/count(%d)/total size(%d)",
+                       (layer_type == BUXTON_LAYER_NORMAL) ?
+                       "NORMAL" : "BASE", key, len, cache->count, cache->size);
+
+}
+
+int cache_get(enum buxton_layer_type layer_type, const char *key,
+               void **data, int *data_len)
+{
+       GHashTable *hash_table = NULL;
+       gpointer raw_data;
+
+       cache_data *cdata;
+       bxt_cache *cache = get_cache(layer_type);
+
+       if (!cache)
+               return -1;
+
+       hash_table = cache->hash_table;
+
+       raw_data =  g_hash_table_lookup(hash_table, key);
+
+       if (!raw_data)
+               return -1;
+
+       cdata = (cache_data *)raw_data;
+
+       *data = malloc(cdata->len);
+       if (!*data)
+               return -1;
+
+       memcpy(*data, cdata->data, cdata->len);
+       *data_len = cdata->len;
+
+       bxt_dbg("[%s cache] get (%s)/size(%d)",
+                       (layer_type == BUXTON_LAYER_NORMAL) ?
+                       "NORMAL" : "BASE", key, cdata->len);
+
+       return 0;
+}
+
+void cache_clear(enum buxton_layer_type layer_type)
+{
+       bxt_cache *cache = get_cache(layer_type);
+
+       if (!cache)
+               return;
+
+       g_hash_table_remove_all(cache->hash_table);
+}
+
diff --git a/common/cache.h b/common/cache.h
new file mode 100644 (file)
index 0000000..47ffbe6
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Buxton
+ *
+ * Copyright (C) 2016 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+void cache_init(void);
+void cache_insert(enum buxton_layer_type layer_type, const char *key,
+               uint8_t *data, int len);
+void cache_remove(enum buxton_layer_type layer_type, const char *key);
+void cache_update_data(enum buxton_layer_type layer_type, const char *key,
+               uint8_t *data, int len);
+int cache_get(enum buxton_layer_type layer_type, const char *key,
+               void **data, int *data_len);
+
+void cache_clear(enum buxton_layer_type layer_type);
index 7004432..9c9e3ad 100644 (file)
@@ -34,6 +34,7 @@
 #include "config.h"
 #include "backends.h"
 #include "serialize.h"
+#include "cache.h"
 
 static int get_path(uid_t uid, enum buxton_layer_type type,
                const struct layer *ly, char *path, int sz)
@@ -82,6 +83,9 @@ static int get_raw(const struct layer *ly, uid_t uid,
                return -1;
        }
 
+       if (!cache_get(type, key, (void **)data, len))
+               return 0;
+
        r = get_path(uid, type, ly, path, sizeof(path));
        if (r == -1)
                return -1;
@@ -94,6 +98,8 @@ static int get_raw(const struct layer *ly, uid_t uid,
                return -1;
        }
 
+       cache_insert(type, key, *data, *len);
+
        return 0;
 }
 
@@ -191,6 +197,35 @@ int direct_check(const struct buxton_layer *layer, const char *key)
        return 0;
 }
 
+static int is_updated_data(enum buxton_layer_type type, const char *key,
+               uint8_t *data, int len)
+{
+       uint8_t *cur_data;
+       int cur_data_len;
+
+       if (cache_get(type, key, (void **)&cur_data, &cur_data_len))
+               return -1;
+
+       if (cur_data_len != len) {
+               if (cur_data)
+                       free(cur_data);
+
+               return -1;
+       }
+
+       if (memcmp(cur_data, data, len)) {
+               if (cur_data)
+                       free(cur_data);
+               return -1;
+       }
+
+       if (cur_data)
+               free(cur_data);
+
+       return 0;
+
+}
+
 static int set_raw(const struct layer *ly, uid_t uid,
                enum buxton_layer_type type, const char *key,
                uint8_t *data, int len)
@@ -212,6 +247,11 @@ static int set_raw(const struct layer *ly, uid_t uid,
                return -1;
        }
 
+       if (!is_updated_data(type, key, data, len)) {
+               bxt_dbg("skip setting (same data) - (%s)", key);
+               return 0;
+       }
+
        r = get_path(uid, type, ly, path, sizeof(path));
        if (r == -1)
                return -1;
@@ -220,6 +260,8 @@ static int set_raw(const struct layer *ly, uid_t uid,
        if (r == -1)
                return -1;
 
+       cache_update_data(type, key, data, len);
+
        return 0;
 }
 
@@ -389,6 +431,8 @@ int direct_unset(const struct buxton_layer *layer, const char *key)
                return -1;
        }
 
+       cache_remove(layer->type, key);
+
        return 0;
 }
 
index 7688cf5..3e36548 100644 (file)
@@ -20,6 +20,7 @@ SET(SRC main.c
        ../common/serialize.c
        ../common/direct.c
        ../common/proto.c
+       ../common/cache.c
 )
 ADD_EXECUTABLE(${TARGET} ${SRC})
 SET_TARGET_PROPERTIES(${TARGET} PROPERTIES
index 6cc5587..e440308 100644 (file)
@@ -39,6 +39,7 @@
 #include "socks.h"
 #include "cynara.h"
 #include "dbus.h"
+#include "cache.h"
 
 struct bxt_noti {
        char *layer_key;
@@ -268,8 +269,6 @@ static void proc_set(struct bxt_client *cli,
                return;
        }
        resp->res = 0;
-
-       send_notis(cli->bxtd, rqst);
 }
 
 static void proc_get(struct bxt_client *cli,
@@ -639,6 +638,9 @@ static void cyn_cb(struct bxt_client *cli, enum buxton_cynara_res res,
 
        send_res(cli, &resp);
 
+       if (rqst->type == MSG_SET)
+               send_notis(cli->bxtd, rqst);
+
        free_response(&resp);
        free_request(rqst);
        free(rqst);
@@ -716,6 +718,9 @@ static int proc_serialized_msg(struct bxt_client *cli, uint8_t *data, int len)
 
        send_res(cli, &resp);
 
+       if (rqst->type == MSG_SET)
+               send_notis(cli->bxtd, rqst);
+
        free_response(&resp);
        free_request(rqst);
        free(rqst);
@@ -962,6 +967,8 @@ int start_daemon(struct bxt_daemon *bxtd, const char *confpath)
                return EXIT_FAILURE;
        }
 
+       cache_init();
+
        g_main_loop_run(bxtd->loop);
        bxt_exit(bxtd);
 
index 34bf0a0..59b2e7b 100644 (file)
@@ -4,6 +4,7 @@ SET(TARGET buxton2)
 SET(SRC buxton2.c
        ../common/proto.c
        ../common/serialize.c
+       ../common/cache.c
        ../common/common.c)
 INCLUDE_DIRECTORIES(include)
 ADD_LIBRARY(${TARGET} SHARED ${SRC})
index cc793b0..3eea5db 100644 (file)
@@ -41,6 +41,8 @@
 
 #define LOG_TAG "VCONF"
 
+#define HASH_SIZE 10
+
 static void _restore_noti_cb(gpointer key, gpointer value, gpointer user_data);
 
 static pthread_mutex_t vconf_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -49,6 +51,8 @@ static struct buxton_client *client;
 static struct buxton_layer *system_layer;
 static struct buxton_layer *memory_layer;
 static GHashTable *noti_tbl;
+static GHashTable *cache_tbl;
+static GQueue *victims;
 
 struct noti {
        char *key;
@@ -201,6 +205,12 @@ static void _close(void)
        g_hash_table_destroy(noti_tbl);
        noti_tbl = NULL;
 
+       g_hash_table_destroy(cache_tbl);
+       cache_tbl = NULL;
+
+       g_queue_free_full(victims, (GDestroyNotify)free);
+       victims = NULL;
+
        buxton_close(client);
        client = NULL;
 }
@@ -255,6 +265,17 @@ static int _open(void)
                                NULL, (GDestroyNotify)free_noti);
        }
 
+       if (!cache_tbl) {
+               cache_tbl = g_hash_table_new_full(g_str_hash, g_str_equal,
+                               (GDestroyNotify)free,
+                               (GDestroyNotify)buxton_value_free);
+       }
+
+       if (!victims) {
+               victims = g_queue_new();
+               g_queue_init(victims);
+       }
+
        if (!system_layer)
                system_layer = buxton_create_layer("system");
        if (!memory_layer)
@@ -638,8 +659,17 @@ static int _vconf_set(const char *key, const struct buxton_value *val)
        }
 
        r = buxton_set_value_sync(client, get_layer(key), key, val);
-       if (r == -1)
+       if (r == -1) {
                LOGE("set value: key '%s' errno %d", key, errno);
+       } else {
+               if (cache_tbl) {
+                       if (g_hash_table_contains(cache_tbl, key)) {
+                               g_hash_table_replace(cache_tbl,
+                                               strdup(key),
+                                               buxton_value_duplicate(val));
+                       }
+               }
+       }
 
        _close();
        pthread_mutex_unlock(&vconf_lock);
@@ -743,6 +773,63 @@ EXPORT int vconf_set_dbl(const char *key, double dblval)
        return r;
 }
 
+static void cache_cb(const struct buxton_layer *layer, const char *key,
+               const struct buxton_value *val, void *user_data)
+{
+       if (g_hash_table_contains(cache_tbl, key)) {
+               g_hash_table_replace(cache_tbl,
+                               strdup(key), buxton_value_duplicate(val));
+       }
+}
+
+
+static void _reduce_cache()
+{
+       char *key;
+       int r;
+
+       key = (char *)g_queue_pop_tail(victims);
+       if (key == NULL)
+               return;
+
+       r = g_hash_table_remove(cache_tbl, key);
+       if (r == false)
+               return;
+
+       r = buxton_unregister_notification_sync(client, get_layer(key),
+                       key, cache_cb);
+       if (r == -1)
+               return;
+
+       _close();
+
+       free(key);
+}
+
+static void _insert_cache(const char *key, struct buxton_value *val)
+{
+       int r;
+
+       r = _open();
+       if (r == -1)
+               return;
+
+       if (g_hash_table_size(cache_tbl) >= HASH_SIZE)
+               _reduce_cache();
+
+       r = buxton_register_notification_sync(client, get_layer(key), key,
+                       cache_cb, NULL);
+
+       if (r == -1) {
+               _close();
+               return;
+       }
+
+       g_queue_push_head(victims, (char *)strdup(key));
+       r = g_hash_table_insert(cache_tbl, strdup(key),
+                       buxton_value_duplicate(val));
+}
+
 static int _vconf_get(const char *key, enum buxton_key_type type,
                struct buxton_value **val)
 {
@@ -753,12 +840,21 @@ static int _vconf_get(const char *key, enum buxton_key_type type,
        assert(val);
 
        pthread_mutex_lock(&vconf_lock);
+
        r = _open();
        if (r == -1) {
                pthread_mutex_unlock(&vconf_lock);
                return -1;
        }
 
+       v = g_hash_table_lookup(cache_tbl, key);
+       if (v) {
+               *val = buxton_value_duplicate(v);
+               _close();
+               pthread_mutex_unlock(&vconf_lock);
+               return 0;
+       }
+
        r = buxton_get_value_sync(client, get_layer(key), key, &v);
        if (r == -1) {
                LOGE("get value: key '%s' errno %d", key, errno);
@@ -778,7 +874,9 @@ static int _vconf_get(const char *key, enum buxton_key_type type,
                }
        }
 
+       _insert_cache(key, v);
        _close();
+
        pthread_mutex_unlock(&vconf_lock);
 
        return r;