[svn-commits] mjordan: branch mjordan/trunk-container-performance r423235 - /team/mjordan/t...
SVN commits to the Digium repositories
svn-commits at lists.digium.com
Tue Sep 16 17:38:59 CDT 2014
Author: mjordan
Date: Tue Sep 16 17:38:57 2014
New Revision: 423235
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=423235
Log:
Add container performance test module
Added:
team/mjordan/trunk-container-performance/tests/test_container_perf.c (with props)
Added: team/mjordan/trunk-container-performance/tests/test_container_perf.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-container-performance/tests/test_container_perf.c?view=auto&rev=423235
==============================================================================
--- team/mjordan/trunk-container-performance/tests/test_container_perf.c (added)
+++ team/mjordan/trunk-container-performance/tests/test_container_perf.c Tue Sep 16 17:38:57 2014
@@ -1,0 +1,640 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2014, Matt Jordan
+ *
+ * Matt Jordan <mjordan at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Relative container performance comparison
+ *
+ * \author\verbatim Matt Jordan <mjordan at digium.com> \endverbatim
+ *
+ * This does some relative performance comparisons of common
+ * Asterisk container types.
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+ <depend>TEST_FRAMEWORK</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <math.h>
+#include "asterisk/utils.h"
+#include "asterisk/module.h"
+#include "asterisk/test.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/dlinkedlists.h"
+#include "asterisk/vector.h"
+#include "asterisk/strings.h"
+
+struct test_item {
+ int unique_id;
+ AST_LIST_ENTRY(test_item) list;
+ AST_DLLIST_ENTRY(test_item) dlist;
+};
+
+struct vector_wrapper {
+ AST_VECTOR(, struct test_item *) vector;
+};
+
+struct linked_list_wrapper {
+ AST_LIST_HEAD_NOLOCK(, test_item) linked_list;
+};
+
+struct dlinked_list_wrapper {
+ AST_DLLIST_HEAD_NOLOCK(, test_item) dlinked_list;
+};
+
+static int hash_sizes[5] = { 11, 101, 1009, 10007, 100003, };
+static int vector_sizes[5] = { 10, 100, 1000, 10000, 100000, };
+
+struct result {
+ char name[32];
+ struct timeval elapsed_time;
+};
+
+static int test_item_hash_fn(const void *obj, const int flags)
+{
+ const struct test_item *item;
+ int key;
+
+ switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
+ case OBJ_KEY:
+ key = *(int *)obj;
+ break;
+ case OBJ_POINTER:
+ item = obj;
+ key = item->unique_id;
+ break;
+ default:
+ ast_assert(0);
+ return 0;
+ }
+ return key;
+}
+
+static int test_item_cmp_fn(void *obj, void *arg, int flags)
+{
+ struct test_item *left = obj;
+ struct test_item *right = arg;
+ int right_key = *(int *)arg;
+ int cmp;
+
+ switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
+ case OBJ_POINTER:
+ right_key = right->unique_id;
+ /* Fall through */
+ case OBJ_KEY:
+ case OBJ_PARTIAL_KEY:
+ default:
+ cmp = (left->unique_id == right_key);
+ break;
+ }
+ return cmp ? (CMP_MATCH | CMP_STOP) : 0;
+}
+
+static int test_item_sort_fn(const void *obj, const void *arg, int flags)
+{
+ const struct test_item *left = obj;
+ const struct test_item *right = arg;
+ int right_key = *(int *)arg;
+ int cmp;
+
+ switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
+ case OBJ_POINTER:
+ right_key = right->unique_id;
+ /* Fall through */
+ case OBJ_KEY:
+ case OBJ_PARTIAL_KEY:
+ default:
+ if (left->unique_id < right_key) {
+ cmp = -1;
+ } else if (left->unique_id > right_key) {
+ cmp = 1;
+ } else {
+ cmp = 0;
+ }
+ break;
+ }
+ return cmp;
+}
+
+
+static void destroy_item_array(struct test_item **item_array, size_t count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ ao2_ref(item_array[i], -1);
+ }
+ ast_free(item_array);
+}
+
+static struct test_item **create_item_array(size_t count)
+{
+ struct test_item **alloc_array;
+ int i;
+
+ alloc_array = ast_calloc(count, sizeof(struct test_item *));
+ if (!alloc_array) {
+ return NULL;
+ }
+
+ for (i = 0; i < count; ++i) {
+ struct test_item *item = ao2_alloc_options(sizeof(*item), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!item) {
+ destroy_item_array(alloc_array, i);
+ return NULL;
+ }
+ item->unique_id = i;
+ alloc_array[i] = item;
+ }
+
+ return alloc_array;
+}
+
+static void insert_vector_cb(void *container, struct test_item *item)
+{
+ struct vector_wrapper *wrapper = container;
+
+ AST_VECTOR_APPEND(&wrapper->vector, item);
+}
+
+static void insert_list_cb(void *container, struct test_item *item)
+{
+ struct linked_list_wrapper *wrapper = container;
+
+ AST_LIST_INSERT_TAIL(&wrapper->linked_list, item, list);
+}
+
+static void insert_dlist_cb(void *container, struct test_item *item)
+{
+ struct dlinked_list_wrapper *wrapper = container;
+
+ AST_DLLIST_INSERT_TAIL(&wrapper->dlinked_list, item, dlist);
+}
+
+
+static void insert_ao2_cb(void *container, struct test_item *item)
+{
+ struct ao2_container *ao2_container = container;
+
+ ao2_link_flags(ao2_container, item, OBJ_NOLOCK);
+}
+
+static struct timeval insert_items(void *container,
+ void (* const insert_cb)(void *container, struct test_item *item),
+ struct test_item **items,
+ size_t count)
+{
+ struct timeval start;
+ struct timeval stop;
+ int i;
+
+ start = ast_tvnow();
+ for (i = 0; i < count; i++) {
+ insert_cb(container, items[i]);
+ }
+ stop = ast_tvnow();
+
+ return ast_tvsub(stop, start);
+}
+
+static void vector_insertion_test(void *obj, struct test_item **item_array, size_t elements, size_t container_size, struct result *result, int destroy)
+{
+ struct vector_wrapper *vec_wrapper = obj;
+
+ snprintf(result->name, sizeof(result->name), "vector_%d", vector_sizes[container_size]);
+
+ AST_VECTOR_INIT(&vec_wrapper->vector, vector_sizes[container_size]);
+ result->elapsed_time = insert_items(vec_wrapper, insert_vector_cb, item_array, elements);
+ if (destroy) {
+ AST_VECTOR_FREE(&vec_wrapper->vector);
+ }
+}
+
+static void ao2_hash_insertion_test(void *obj, struct test_item **item_array, size_t elements, size_t container_size, struct result *result, int destroy)
+{
+ struct ao2_container *container = obj;
+
+ snprintf(result->name, sizeof(result->name), "ao2hash_%d", hash_sizes[container_size]);
+
+ container = ao2_container_alloc_hash(OBJ_NOLOCK, 0, hash_sizes[container_size], test_item_hash_fn, test_item_sort_fn, test_item_cmp_fn);
+ if (!container) {
+ return;
+ }
+ result->elapsed_time = insert_items(container, insert_ao2_cb, item_array, elements);
+ if (destroy) {
+ ao2_ref(container, -1);
+ }
+}
+
+static void ao2_list_insertion_test(void *obj, struct test_item **item_array, size_t elements, size_t container_size, struct result *result, int destroy)
+{
+ struct ao2_container *container = obj;
+
+ snprintf(result->name, sizeof(result->name), "ao2list");
+
+ container = ao2_container_alloc_list(OBJ_NOLOCK, 0, test_item_sort_fn, test_item_cmp_fn);
+ if (!container) {
+ return;
+ }
+ result->elapsed_time = insert_items(container, insert_ao2_cb, item_array, elements);
+ if (destroy) {
+ ao2_ref(container, -1);
+ }
+}
+
+static void ao2_rb_insertion_test(void *obj, struct test_item **item_array, size_t elements, size_t container_size, struct result *result, int destroy)
+{
+ struct ao2_container *container = obj;
+
+ snprintf(result->name, sizeof(result->name), "ao2rbtree");
+
+ container = ao2_container_alloc_rbtree(OBJ_NOLOCK, 0, test_item_sort_fn, test_item_cmp_fn);
+ if (!container) {
+ return;
+ }
+ result->elapsed_time = insert_items(container, insert_ao2_cb, item_array, elements);
+ if (destroy) {
+ ao2_ref(container, -1);
+ }
+}
+
+static void linked_list_insertion_test(void *obj, struct test_item **item_array, size_t elements, size_t container_size, struct result *result, int destroy)
+{
+ struct test_item *current;
+ struct linked_list_wrapper *list_wrapper = obj;
+
+ snprintf(result->name, sizeof(result->name), "llist");
+
+ result->elapsed_time = insert_items(list_wrapper, insert_list_cb, item_array, elements);
+
+ if (destroy) {
+ while ((current = AST_LIST_REMOVE_HEAD(&list_wrapper->linked_list, list)));
+ }
+}
+
+static void dlinked_list_insertion_test(void *obj, struct test_item **item_array, size_t elements, size_t container_size, struct result *result, int destroy)
+{
+ struct test_item *current;
+ struct dlinked_list_wrapper *dlist_wrapper = obj;
+
+ snprintf(result->name, sizeof(result->name), "dlist");
+
+ result->elapsed_time = insert_items(dlist_wrapper, insert_dlist_cb, item_array, elements);
+
+ if (destroy) {
+ while ((current = AST_DLLIST_REMOVE_HEAD(&dlist_wrapper->dlinked_list, dlist)));
+ }
+}
+
+
+AST_TEST_DEFINE(test_container_insertion)
+{
+ struct ao2_container *ao2_container = NULL;
+ struct dlinked_list_wrapper dlist_wrapper = { .dlinked_list = AST_DLLIST_HEAD_NOLOCK_INIT_VALUE, };
+ struct linked_list_wrapper list_wrapper = { .linked_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE, };
+ struct vector_wrapper vec_wrapper;
+ size_t elements = 100000;
+ struct test_item **item_array;
+ int i, j, k;
+ RAII_VAR(struct ast_str *, output, ast_str_create(1024), ast_free);
+ struct {
+ int subtest;
+ void *container;
+ void (* const test_fn)(void *obj, struct test_item **item_array, size_t elements, size_t container, struct result *result, int destroy);
+ } tests[6] = { { .container = &vec_wrapper, .test_fn = vector_insertion_test, .subtest = 1, },
+ { .container = &list_wrapper, .test_fn = linked_list_insertion_test, },
+ { .container = &dlist_wrapper, .test_fn = dlinked_list_insertion_test, },
+ { .container = ao2_container, .test_fn = ao2_hash_insertion_test, .subtest = 1, },
+ { .container = ao2_container, .test_fn = ao2_list_insertion_test, },
+ { .container = ao2_container, .test_fn = ao2_rb_insertion_test, }, };
+
+ struct result results[6][5][5];
+ memset(results, 0, sizeof(results));
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = "/perf/container";
+ info->summary = "Test insertion performance";
+ info->description =
+ "This tests insertion with a variable number of elements\n"
+ "for a variety of containers/sizes";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ item_array = create_item_array(elements);
+ if (!item_array) {
+ return AST_TEST_FAIL;
+ }
+
+ ast_test_status_update(test, "Starting insertion test\n");
+ for (i = 0; i < 6; i++) {
+ for (j = 0; j < 5; j++) {
+ size_t elements = (size_t)pow(10, (j + 1));
+ for (k = 0; k < 5; k++) {
+ if (!tests[i].subtest && k > 0) {
+ break;
+ }
+ tests[i].test_fn(tests[i].container, item_array, elements, k, &results[i][j][k], 1);
+ ast_test_status_update(test, "Tested %s with %zd elements\n", results[i][j][k].name, elements);
+ }
+ }
+ }
+ ast_test_status_update(test, "-- RESULTS --\n");
+ ast_str_append(&output, 0, "\n%18.18s", " ");
+ for (j = 0; j < 5; j++) {
+ size_t elements = (size_t)pow(10, (j + 1));
+ ast_str_append(&output, 0, "% 13zd", elements);
+ }
+ ast_str_append(&output, 0, "\n");
+ ast_test_status_update(test, "\n");
+ for (i = 0; i < 6; i++) {
+ for (k = 0; k < 5; k++) {
+ int skipped = 0;
+ for (j = 0; j < 5; j++) {
+ if (ast_tvzero(results[i][j][k].elapsed_time)) {
+ skipped = 1;
+ break;
+ }
+ if (j == 0 && (k == 0 || tests[i].subtest)) {
+ ast_str_append(&output, 0, "%18.18s", results[i][j][k].name);
+ }
+ ast_str_append(&output, 0, "%6ld.%06ld", results[i][j][k].elapsed_time.tv_sec, results[i][j][k].elapsed_time.tv_usec);
+ }
+ if (!skipped) {
+ ast_str_append(&output, 0, "\n");
+ }
+ }
+ }
+ ast_test_status_update(test, "%s\n", ast_str_buffer(output));
+ destroy_item_array(item_array, elements);
+ return AST_TEST_PASS;
+}
+/*
+static void vector_dtor(void *obj)
+{
+ struct vector_wrapper *vec_wrapper = obj;
+
+ AST_VECTOR_FREE(&vec_wrapper->vector);
+}
+
+static void linked_list_dtor(void *obj)
+{
+ struct test_item *current;
+ struct linked_list_wrapper *list_wrapper = obj;
+
+ while ((current = AST_LIST_REMOVE_HEAD(&list_wrapper->linked_list, list)));
+}
+
+static void dlinked_list_dtor(void *obj)
+{
+ struct test_item *current;
+ struct dlinked_list_wrapper *dlist_wrapper = obj;
+
+ while ((current = AST_DLLIST_REMOVE_HEAD(&dlist_wrapper->dlinked_list, dlist)));
+}
+*/
+static void ao2_dtor(void *obj)
+{
+ struct ao2_container *container = obj;
+ ao2_ref(container, -1);
+}
+/*
+static void vector_lookup_test(void *obj, struct test_item **item_array, size_t elements, struct result *result)
+{
+ struct vector_wrapper *vec_wrapper = obj;
+ struct test_item *item;
+ int i, j;
+ struct timeval start;
+ struct timeval stop;
+ int found = 0;
+
+ start = ast_tvnow();
+ for (i = 0; i < elements; i++) {
+ for (j = 0; j < AST_VECTOR_SIZE(&vec_wrapper->vector); j++) {
+ item = AST_VECTOR_GET(&vec_wrapper->vector, j);
+ if (item_array[i]->unique_id == item->unique_id) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ ast_assert(0);
+ }
+ found = 0;
+ }
+ stop = ast_tvnow();
+
+ result->elapsed_time = ast_tvsub(start, stop);
+}
+
+static void linked_list_lookup_test(void *obj, struct test_item **item_array, size_t elements, struct result *result)
+{
+ struct linked_list_wrapper *list_wrapper = obj;
+ struct test_item *item;
+ int i;
+ struct timeval start;
+ struct timeval stop;
+ int found = 0;
+
+ start = ast_tvnow();
+ for (i = 0; i < elements; i++) {
+ AST_LIST_TRAVERSE(&list_wrapper->linked_list, item, list) {
+ if (item_array[i]->unique_id == item->unique_id) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ ast_assert(0);
+ }
+ found = 0;
+ }
+ stop = ast_tvnow();
+
+ result->elapsed_time = ast_tvsub(start, stop);
+}
+
+static void dlinked_list_lookup_test(void *obj, struct test_item **item_array, size_t elements, struct result *result)
+{
+ struct dlinked_list_wrapper *dlist_wrapper = obj;
+ struct test_item *item;
+ int i;
+ struct timeval start;
+ struct timeval stop;
+ int found = 0;
+
+ start = ast_tvnow();
+ for (i = 0; i < elements; i++) {
+ AST_DLLIST_TRAVERSE(&dlist_wrapper->dlinked_list, item, dlist) {
+ if (item_array[i]->unique_id == item->unique_id) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ ast_assert(0);
+ }
+ found = 0;
+ }
+ stop = ast_tvnow();
+
+ result->elapsed_time = ast_tvsub(start, stop);
+}*/
+
+static void ao2_lookup_test(void *obj, struct test_item **item_array, size_t elements, struct result *result)
+{
+ struct ao2_container *container = obj;
+ struct test_item *item;
+ struct timeval start;
+ struct timeval stop;
+ int i;
+
+ start = ast_tvnow();
+ for (i = 0; i < elements; i++) {
+ item = ao2_find(container, &item_array[i]->unique_id, OBJ_KEY | OBJ_NOLOCK);
+ if (!item) {
+ ast_assert(0);
+ }
+ ao2_ref(item, -1);
+ }
+ stop = ast_tvnow();
+
+ result->elapsed_time = ast_tvsub(start, stop);
+}
+
+AST_TEST_DEFINE(test_container_lookup)
+{
+ struct ao2_container *ao2_container = NULL;
+ /*struct dlinked_list_wrapper dlist_wrapper = { .dlinked_list = AST_DLLIST_HEAD_NOLOCK_INIT_VALUE, };
+ struct linked_list_wrapper list_wrapper = { .linked_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE, };
+ struct vector_wrapper vec_wrapper;*/
+ size_t elements = 100000;
+ struct test_item **item_array;
+ int i, j, k;
+ RAII_VAR(struct ast_str *, output, ast_str_create(1024), ast_free);
+ struct {
+ int subtest;
+ void *container;
+ void (* const build_fn)(void *obj, struct test_item **item_array, size_t elements, size_t container, struct result *result, int destroy);
+ void (* const test_fn)(void *obj, struct test_item **item_array, size_t elements, struct result *result);
+ void (* const dtor_fn)(void *obj);
+ } tests[3] = { /*{ .container = &vec_wrapper, .build_fn = vector_insertion_test, .test_fn = vector_lookup_test, .dtor_fn = vector_dtor, .subtest = 1, },
+ { .container = &list_wrapper, .build_fn = linked_list_insertion_test, .test_fn = linked_list_lookup_test, .dtor_fn = linked_list_dtor, },
+ { .container = &dlist_wrapper, .build_fn = dlinked_list_insertion_test, .test_fn = dlinked_list_lookup_test, .dtor_fn = dlinked_list_dtor, },*/
+ { .container = &ao2_container, .build_fn = ao2_hash_insertion_test, .test_fn = ao2_lookup_test, .dtor_fn = ao2_dtor, .subtest = 1, },
+ { .container = &ao2_container, .build_fn = ao2_list_insertion_test, .test_fn = ao2_lookup_test, .dtor_fn = ao2_dtor, },
+ { .container = &ao2_container, .build_fn = ao2_rb_insertion_test, .test_fn = ao2_lookup_test, .dtor_fn = ao2_dtor, }, };
+
+ struct result results[6][5][5];
+ memset(results, 0, sizeof(results));
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = "/perf/container";
+ info->summary = "Test lookup performance";
+ info->description =
+ "This tests item selection with a variable number of elements\n"
+ "for a variety of containers/sizes";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ item_array = create_item_array(elements);
+ if (!item_array) {
+ return AST_TEST_FAIL;
+ }
+
+ ast_test_status_update(test, "Starting lookup test\n");
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 5; j++) {
+ size_t elements = (size_t)pow(10, (j + 1));
+ for (k = 0; k < 5; k++) {
+ if (!tests[i].subtest && k > 0) {
+ break;
+ }
+ tests[i].build_fn(tests[i].container, item_array, elements, k, &results[i][j][k], 0);
+ results[i][j][k].elapsed_time.tv_sec = 0;
+ results[i][j][k].elapsed_time.tv_usec = 0;
+ ast_test_status_update(test, "Container: %p\n", tests[i].container);
+ tests[i].test_fn(tests[i].container, item_array, elements, &results[i][j][k]);
+ ast_test_status_update(test, "Container: %p\n", tests[i].container);
+ tests[i].dtor_fn(tests[i].container);
+ ast_test_status_update(test, "Tested %s with %zd elements\n", results[i][j][k].name, elements);
+ }
+ }
+ }
+ ast_test_status_update(test, "-- RESULTS --\n");
+ ast_str_append(&output, 0, "\n%18.18s", " ");
+ for (j = 0; j < 5; j++) {
+ size_t elements = (size_t)pow(10, (j + 1));
+ ast_str_append(&output, 0, "% 13zd", elements);
+ }
+ ast_str_append(&output, 0, "\n");
+ ast_test_status_update(test, "\n");
+ for (i = 0; i < 6; i++) {
+ for (k = 0; k < 5; k++) {
+ int skipped = 0;
+ for (j = 0; j < 5; j++) {
+ if (ast_tvzero(results[i][j][k].elapsed_time)) {
+ skipped = 1;
+ break;
+ }
+ if (j == 0 && (k == 0 || tests[i].subtest)) {
+ ast_str_append(&output, 0, "%18.18s", results[i][j][k].name);
+ }
+ ast_str_append(&output, 0, "%6ld.%06ld", results[i][j][k].elapsed_time.tv_sec, results[i][j][k].elapsed_time.tv_usec);
+ }
+ if (!skipped) {
+ ast_str_append(&output, 0, "\n");
+ }
+ }
+ }
+ ast_test_status_update(test, "%s\n", ast_str_buffer(output));
+ destroy_item_array(item_array, elements);
+ return AST_TEST_PASS;
+}
+
+
+
+static int unload_module(void)
+{
+ AST_TEST_UNREGISTER(test_container_insertion);
+ AST_TEST_UNREGISTER(test_container_lookup);
+ return 0;
+}
+
+static int load_module(void)
+{
+ AST_TEST_REGISTER(test_container_insertion);
+ AST_TEST_REGISTER(test_container_lookup);
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Skeleton (sample) Test");
Propchange: team/mjordan/trunk-container-performance/tests/test_container_perf.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: team/mjordan/trunk-container-performance/tests/test_container_perf.c
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: team/mjordan/trunk-container-performance/tests/test_container_perf.c
------------------------------------------------------------------------------
svn:mime-type = text/plain
More information about the svn-commits
mailing list