diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 08ddbfae3..42f7cb742 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -85,10 +85,13 @@ endif() # sr_perf benchmark binary if(ENABLE_PERF_TESTS) add_executable(sr_perf ${CMAKE_CURRENT_SOURCE_DIR}/perf.c) - target_link_libraries(sr_perf sysrepo) + target_link_libraries(sr_perf sysrepo srobj) - add_test(NAME sr_perf_1000 COMMAND sr_perf 1000 10) - add_test(NAME sr_perf_100000 COMMAND sr_perf 100000 3) + add_test(NAME sr_perf_1000_10 COMMAND sr_perf 1000 10) + + if(${CMAKE_VERSION} VERSION_GREATER "3.7") + set_tests_properties(sr_perf_1000_10 PROPERTIES FIXTURES_REQUIRED tests_cleanup) + endif() endif() # valgrind tests diff --git a/tests/files/perf.yang b/tests/files/perf.yang index 96149458a..7e9168d45 100644 --- a/tests/files/perf.yang +++ b/tests/files/perf.yang @@ -15,6 +15,18 @@ module perf { type string; } + leaf l { + type string; + } + } + list usr-lst { + key k; + ordered-by user; + + leaf k { + type uint32; + } + leaf l { type string; } diff --git a/tests/perf.c b/tests/perf.c index 242efd6eb..8d42eaf5e 100644 --- a/tests/perf.c +++ b/tests/perf.c @@ -1,6 +1,7 @@ /** * @file perf.c * @author Michal Vasko + * @author Ondrej Kusnirik * @brief performance tests * * Copyright (c) 2021 CESNET, z.s.p.o. @@ -23,7 +24,9 @@ #include +#include "common.h" #include "config.h" +#include "plugins_datastore.h" #include "sysrepo.h" #include "tests/tcommon.h" @@ -31,6 +34,13 @@ # include #endif +#define TABLE_WIDTH 67 +#define NAME_FIXED_LEN 33 +#define COL_FIXED_LEN 15 +#define MILLION 1000000 + +#define ABS(x) (x < 0) * (-x) + (x >= 0) * x + /** * @brief Test state structure. */ @@ -42,9 +52,9 @@ struct test_state { uint32_t count; }; -typedef int (*setup_cb)(uint32_t count, struct test_state *state); - +typedef int (*setup_cb)(struct test_state *state); typedef int (*test_cb)(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end); +typedef void (*teardown_cb)(struct test_state *state); /** * @brief Single test structure. @@ -53,6 +63,7 @@ struct test { const char *name; setup_cb setup; test_cb test; + teardown_cb teardown; }; /** @@ -91,16 +102,14 @@ static uint64_t time_diff(const struct timespec *ts1, const struct timespec *ts2) { uint64_t usec_diff = 0; - int64_t nsec_diff; - + assert(ts1->tv_sec <= ts2->tv_sec); /* seconds diff */ usec_diff += (ts2->tv_sec - ts1->tv_sec) * 1000000; /* nanoseconds diff */ - nsec_diff = ts2->tv_nsec - ts1->tv_nsec; - usec_diff += nsec_diff ? nsec_diff / 1000 : 0; + usec_diff += (ts2->tv_nsec - ts1->tv_nsec) / 1000; return usec_diff; } @@ -141,6 +150,41 @@ create_list_inst(const struct lys_module *mod, uint32_t offset, uint32_t count, return SR_ERR_OK; } +/** + * @brief Create data tree with user ordered list instances. + * + * @param[in] mod Module of the top-level node. + * @param[in] offset Starting offset of the identifier number values. + * @param[in] count Number of list instances to create, with increasing identifier numbers. + * @param[out] data Created data. + * @return SR ERR value. + */ +static int +create_user_order_list_inst(const struct lys_module *mod, uint32_t offset, uint32_t count, struct lyd_node **data) +{ + uint32_t i; + char k_val[32], l_val[32]; + struct lyd_node *list; + + if (lyd_new_inner(NULL, mod, "cont", 0, data)) { + return SR_ERR_LY; + } + + for (i = 0; i < count; ++i) { + sprintf(k_val, "%" PRIu32, i + offset); + sprintf(l_val, "l%" PRIu32, i + offset); + + if (lyd_new_list(*data, NULL, "usr-lst", 0, &list, k_val)) { + return SR_ERR_LY; + } + if (lyd_new_term(list, NULL, "l", l_val, 0, NULL)) { + return SR_ERR_LY; + } + } + + return SR_ERR_OK; +} + /** * @brief Execute a test. * @@ -152,52 +196,32 @@ create_list_inst(const struct lys_module *mod, uint32_t offset, uint32_t count, * @return SR ERR value. */ static int -exec_test(setup_cb setup, test_cb test, const char *name, uint32_t count, uint32_t tries) +exec_test(setup_cb setup, test_cb test, teardown_cb teardown, uint32_t tries, int64_t *time, struct test_state *state) { int ret; struct timespec ts_start, ts_end; - struct test_state state = {0}; - const uint32_t name_fixed_len = 37; - char str[name_fixed_len + 1]; - uint32_t i, printed; + uint32_t i; uint64_t time_usec = 0; - /* print test start */ - printed = sprintf(str, "| %s ", name); - while (printed + 2 < name_fixed_len) { - printed += sprintf(str + printed, "."); - } - if (printed + 1 < name_fixed_len) { - printed += sprintf(str + printed, " "); - } - sprintf(str + printed, "|"); - fputs(str, stdout); - fflush(stdout); - /* setup */ - if ((ret = setup(count, &state))) { + if ((ret = setup(state))) { return ret; } /* test */ for (i = 0; i < tries; ++i) { - if ((ret = test(&state, &ts_start, &ts_end))) { + if ((ret = test(state, &ts_start, &ts_end))) { return ret; } time_usec += time_diff(&ts_start, &ts_end); } time_usec /= tries; - /* teardown */ - sr_delete_item(state.sess, "/perf:cont", 0); - sr_apply_changes(state.sess, 0); - sr_unsubscribe(state.sub); - sr_release_context(state.conn); - sr_disconnect(state.conn); - - /* print time */ - printf(" %3" PRIu64 ".%06" PRIu64 " s |\n", time_usec / 1000000, time_usec % 1000000); + /* save time for later printing */ + *time = (int64_t)time_usec; + /* teardown */ + teardown(state); return SR_ERR_OK; } @@ -223,98 +247,67 @@ TEST_END(struct timespec *ts) /* TEST SYSREPO CB */ static int -change_item_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name, const char *xpath, sr_event_t event, - uint32_t request_id, void *private_data) +oper_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name, const char *path, + const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data) { - int r; - char change_path[32]; - sr_change_iter_t *it = NULL; - sr_change_oper_t oper; - sr_val_t *old_value = NULL; - sr_val_t *new_value = NULL; + struct test_state *state = private_data; + (void)session; (void)sub_id; - (void)xpath; - (void)event; + (void)module_name; + (void)path; + (void)request_xpath; (void)request_id; - (void)private_data; - - sprintf(change_path, "/%s:*//.", module_name); - if ((r = sr_get_changes_iter(session, change_path, &it))) { - goto cleanup; - } - - while (!sr_get_change_next(session, it, &oper, &old_value, &new_value)) { - sr_free_val(old_value); - sr_free_val(new_value); - } - -cleanup: - sr_free_change_iter(it); - return SR_ERR_OK; + return create_list_inst(state->mod, 0, state->count, parent); } +/* TEST SETUPS */ static int -change_tree_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name, const char *xpath, sr_event_t event, - uint32_t request_id, void *private_data) +setup_empty(struct test_state *state) { int r; - char change_path[32]; - sr_change_iter_t *it = NULL; - sr_change_oper_t oper; - const struct lyd_node *node; - (void)sub_id; - (void)xpath; - (void)event; - (void)request_id; - (void)private_data; - - sprintf(change_path, "/%s:*//.", module_name); - - if ((r = sr_get_changes_iter(session, change_path, &it))) { - goto cleanup; + if ((r = sr_connect(SR_CONN_DEFAULT, &state->conn))) { + return r; } + if ((r = sr_session_start(state->conn, SR_DS_RUNNING, &(state->sess)))) { + return r; + } + state->mod = ly_ctx_get_module_implemented(sr_acquire_context(state->conn), "perf"); - while (!sr_get_change_tree_next(session, it, &oper, &node, NULL, NULL, NULL)) {} - -cleanup: - sr_free_change_iter(it); return SR_ERR_OK; } static int -oper_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name, const char *path, - const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data) +setup_empty_oper(struct test_state *state) { - struct test_state *state = private_data; + int r; - (void)session; - (void)sub_id; - (void)module_name; - (void)path; - (void)request_xpath; - (void)request_id; + if ((r = sr_connect(SR_CONN_DEFAULT, &state->conn))) { + return r; + } + if ((r = sr_session_start(state->conn, SR_DS_OPERATIONAL, &(state->sess)))) { + return r; + } + state->mod = ly_ctx_get_module_implemented(sr_acquire_context(state->conn), "perf"); - return create_list_inst(state->mod, 0, state->count, parent); + return SR_ERR_OK; } -/* TEST SETUP */ static int -setup_running(uint32_t count, struct test_state *state) +setup_running(struct test_state *state) { int r; struct lyd_node *data; - if ((r = sr_connect(0, &state->conn))) { + if ((r = sr_connect(SR_CONN_DEFAULT, &state->conn))) { return r; } - if ((r = sr_session_start(state->conn, SR_DS_RUNNING, &state->sess))) { + if ((r = sr_session_start(state->conn, SR_DS_RUNNING, &(state->sess)))) { return r; } state->mod = ly_ctx_get_module_implemented(sr_acquire_context(state->conn), "perf"); - state->count = count; /* set running data */ if ((r = create_list_inst(state->mod, 0, state->count, &data))) { @@ -332,10 +325,11 @@ setup_running(uint32_t count, struct test_state *state) } static int -setup_running_cached(uint32_t count, struct test_state *state) +setup_running_cached(struct test_state *state) { int r; struct lyd_node *data; + sr_data_t *retr_data; if ((r = sr_connect(SR_CONN_CACHE_RUNNING, &state->conn))) { return r; @@ -344,7 +338,6 @@ setup_running_cached(uint32_t count, struct test_state *state) return r; } state->mod = ly_ctx_get_module_implemented(sr_acquire_context(state->conn), "perf"); - state->count = count; /* set running data */ if ((r = create_list_inst(state->mod, 0, state->count, &data))) { @@ -358,55 +351,79 @@ setup_running_cached(uint32_t count, struct test_state *state) } lyd_free_siblings(data); + /* get data, so that if connection is cached, cache gets filled */ + if ((r = sr_get_data(state->sess, "//.", 0, 0, 0, &retr_data))) { + return r; + } + sr_release_data(retr_data); + return SR_ERR_OK; } static int -setup_subscribe_change_item(uint32_t count, struct test_state *state) +setup_oper(struct test_state *state) { int r; + struct lyd_node *data; - if ((r = sr_connect(0, &state->conn))) { + if ((r = sr_connect(SR_CONN_DEFAULT, &state->conn))) { return r; } - if ((r = sr_session_start(state->conn, SR_DS_RUNNING, &state->sess))) { + if ((r = sr_session_start(state->conn, SR_DS_OPERATIONAL, &(state->sess)))) { + return r; + } + state->mod = ly_ctx_get_module_implemented(sr_acquire_context(state->conn), "perf"); + + /* set operational data */ + if ((r = create_list_inst(state->mod, 0, state->count, &data))) { return r; } - if ((r = sr_module_change_subscribe(state->sess, "perf", NULL, change_item_cb, NULL, 0, 0, &state->sub))) { + if ((r = sr_edit_batch(state->sess, data, "merge"))) { return r; } - state->mod = ly_ctx_get_module_implemented(sr_acquire_context(state->conn), "perf"); - state->count = count; + if ((r = sr_apply_changes(state->sess, 0))) { + return r; + } + lyd_free_siblings(data); return SR_ERR_OK; } static int -setup_subscribe_change_tree(uint32_t count, struct test_state *state) +setup_userordered_running(struct test_state *state) { int r; + struct lyd_node *data; - if ((r = sr_connect(0, &state->conn))) { + if ((r = sr_connect(SR_CONN_DEFAULT, &state->conn))) { return r; } - if ((r = sr_session_start(state->conn, SR_DS_RUNNING, &state->sess))) { + if ((r = sr_session_start(state->conn, SR_DS_RUNNING, &(state->sess)))) { return r; } - if ((r = sr_module_change_subscribe(state->sess, "perf", NULL, change_tree_cb, NULL, 0, 0, &state->sub))) { + state->mod = ly_ctx_get_module_implemented(sr_acquire_context(state->conn), "perf"); + + /* set running data */ + if ((r = create_user_order_list_inst(state->mod, 0, state->count, &data))) { return r; } - state->mod = ly_ctx_get_module_implemented(sr_acquire_context(state->conn), "perf"); - state->count = count; + if ((r = sr_edit_batch(state->sess, data, "merge"))) { + return r; + } + if ((r = sr_apply_changes(state->sess, 0))) { + return r; + } + lyd_free_siblings(data); return SR_ERR_OK; } static int -setup_subscribe_oper(uint32_t count, struct test_state *state) +setup_subscribe_oper(struct test_state *state) { int r; - if ((r = sr_connect(0, &state->conn))) { + if ((r = sr_connect(SR_CONN_DEFAULT, &state->conn))) { return r; } if ((r = sr_session_start(state->conn, SR_DS_RUNNING, &state->sess))) { @@ -416,12 +433,49 @@ setup_subscribe_oper(uint32_t count, struct test_state *state) return r; } state->mod = ly_ctx_get_module_implemented(sr_acquire_context(state->conn), "perf"); - state->count = count; return SR_ERR_OK; } -/* TEST CB */ +static void +teardown_empty(struct test_state *state) +{ + sr_session_stop(state->sess); + sr_release_context(state->conn); + sr_disconnect(state->conn); +} + +static void +teardown_running(struct test_state *state) +{ + sr_delete_item(state->sess, "/perf:cont", 0); + sr_apply_changes(state->sess, 0); + sr_session_stop(state->sess); + sr_release_context(state->conn); + sr_disconnect(state->conn); +} + +static void +teardown_oper(struct test_state *state) +{ + sr_discard_items(state->sess, "/perf:cont"); + sr_apply_changes(state->sess, 0); + sr_session_stop(state->sess); + sr_release_context(state->conn); + sr_disconnect(state->conn); +} + +static void +teardown_subscribe_oper(struct test_state *state) +{ + sr_session_switch_ds(state->sess, SR_DS_RUNNING); + sr_unsubscribe(state->sub); + state->sub = NULL; + sr_session_stop(state->sess); + sr_release_context(state->conn); + sr_disconnect(state->conn); +} + static int test_get_tree(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) { @@ -489,7 +543,44 @@ test_get_tree_hash(struct test_state *state, struct timespec *ts_start, struct t } static int -test_edit_item_create(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +test_get_user_order_tree(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + int r; + sr_data_t *data; + + TEST_START(ts_start); + + if ((r = sr_get_data(state->sess, "/perf:cont", 0, 0, 0, &data))) { + return r; + } + + TEST_END(ts_end); + + sr_release_data(data); + + return SR_ERR_OK; +} + +static int +test_get_oper_tree(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + int r; + sr_data_t *data; + + TEST_START(ts_start); + + if ((r = sr_get_data(state->sess, "/perf:cont", 0, 0, 0, &data))) { + return r; + } + + TEST_END(ts_end); + + sr_release_data(data); + return SR_ERR_OK; +} + +static int +test_batch_create(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) { int r; struct lyd_node *data; @@ -504,7 +595,7 @@ test_edit_item_create(struct test_state *state, struct timespec *ts_start, struc return r; } - if ((r = sr_apply_changes(state->sess, state->count * 100))) { + if ((r = sr_apply_changes(state->sess, 0))) { return r; } @@ -515,7 +606,73 @@ test_edit_item_create(struct test_state *state, struct timespec *ts_start, struc if ((r = sr_delete_item(state->sess, "/perf:cont/lst", 0))) { return r; } - if ((r = sr_apply_changes(state->sess, state->count * 100))) { + if ((r = sr_apply_changes(state->sess, 0))) { + return r; + } + + return SR_ERR_OK; +} + +static int +test_user_order_items_create(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + int r; + uint32_t i; + char path[64], l_val[32]; + + TEST_START(ts_start); + + for (i = 0; i < state->count; ++i) { + sprintf(path, "/perf:cont/usr-lst[k='%" PRIu32 "']/l", i); + sprintf(l_val, "l%" PRIu32, i); + if ((r = sr_set_item_str(state->sess, path, l_val, NULL, 0))) { + return r; + } + } + + if ((r = sr_apply_changes(state->sess, 0))) { + return r; + } + + TEST_END(ts_end); + + if ((r = sr_delete_item(state->sess, "/perf:cont/usr-lst", 0))) { + return r; + } + if ((r = sr_apply_changes(state->sess, 0))) { + return r; + } + + return SR_ERR_OK; +} + +static int +test_items_create(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + int r; + uint32_t i; + char path[64], l_val[32]; + + TEST_START(ts_start); + + for (i = 0; i < state->count; ++i) { + sprintf(path, "/perf:cont/lst[k1='%" PRIu32 "'][k2='str%" PRIu32 "']/l", i, i); + sprintf(l_val, "l%" PRIu32, i); + if ((r = sr_set_item_str(state->sess, path, l_val, NULL, 0))) { + return r; + } + } + + if ((r = sr_apply_changes(state->sess, 0))) { + return r; + } + + TEST_END(ts_end); + + if ((r = sr_delete_item(state->sess, "/perf:cont/lst", 0))) { + return r; + } + if ((r = sr_apply_changes(state->sess, 0))) { return r; } @@ -523,7 +680,7 @@ test_edit_item_create(struct test_state *state, struct timespec *ts_start, struc } static int -test_edit_batch_create(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +test_items_create_oper(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) { int r; uint32_t i; @@ -539,59 +696,168 @@ test_edit_batch_create(struct test_state *state, struct timespec *ts_start, stru } } - if ((r = sr_apply_changes(state->sess, state->count * 100))) { + if ((r = sr_apply_changes(state->sess, 0))) { return r; } TEST_END(ts_end); + if ((r = sr_discard_items(state->sess, "/perf:cont/lst"))) { + return r; + } + if ((r = sr_apply_changes(state->sess, 0))) { + return r; + } + + return SR_ERR_OK; +} + +static int +test_items_remove(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + int r; + + TEST_START(ts_start); + if ((r = sr_delete_item(state->sess, "/perf:cont/lst", 0))) { return r; } - if ((r = sr_apply_changes(state->sess, state->count * 100))) { + if ((r = sr_apply_changes(state->sess, 0))) { return r; } + TEST_END(ts_end); + return SR_ERR_OK; } static int -test_oper_get_tree(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +test_item_create(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) { int r; + char path[64], l_val[32]; sr_data_t *data; - sr_session_switch_ds(state->sess, SR_DS_OPERATIONAL); + sprintf(path, "/perf:cont/lst[k1='%" PRIu32 "'][k2='str%" PRIu32 "']/l", state->count/2, state->count/2); + if ((r = sr_delete_item(state->sess, path, 0))) { + return r; + } + if ((r = sr_apply_changes(state->sess, 0))) { + return r; + } + + /* get data, so that if connection is cached, cache gets filled */ + if ((r = sr_get_data(state->sess, "//.", 0, 0, 0, &data))) { + return r; + } TEST_START(ts_start); - if ((r = sr_get_data(state->sess, "/perf:cont", 0, 0, 0, &data))) { + sprintf(l_val, "l%" PRIu32, 0); + if ((r = sr_set_item_str(state->sess, path, l_val, NULL, SR_EDIT_STRICT))) { + return r; + } + if ((r = sr_apply_changes(state->sess, 0))) { return r; } TEST_END(ts_end); sr_release_data(data); - sr_session_switch_ds(state->sess, SR_DS_RUNNING); return SR_ERR_OK; } -struct test tests[] = { - {"get tree", setup_running, test_get_tree}, - {"get item", setup_running, test_get_item}, - {"get tree hash", setup_running, test_get_tree_hash}, - {"get tree hash cached", setup_running_cached, test_get_tree_hash}, - {"edit item create", setup_subscribe_change_item, test_edit_item_create}, - {"edit batch create", setup_subscribe_change_tree, test_edit_batch_create}, - {"oper get tree", setup_subscribe_oper, test_oper_get_tree}, -}; +static int +test_item_create_oper(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + int r; + char path[64], l_val[32]; + sr_data_t *data; + + sprintf(path, "/perf:cont/lst[k1='%" PRIu32 "'][k2='str%" PRIu32 "']/l", state->count/2, state->count/2); + if ((r = sr_discard_items(state->sess, path))) { + return r; + } + if ((r = sr_apply_changes(state->sess, 0))) { + return r; + } + + /* get data, so that if connection is cached, cache gets filled */ + if ((r = sr_get_data(state->sess, "//.", 0, 0, 0, &data))) { + return r; + } + + TEST_START(ts_start); + + sprintf(l_val, "l%" PRIu32, 0); + if ((r = sr_set_item_str(state->sess, path, l_val, NULL, 0))) { + return r; + } + if ((r = sr_apply_changes(state->sess, 0))) { + return r; + } + + TEST_END(ts_end); + + sr_release_data(data); + + return SR_ERR_OK; +} static int -sysrepo_init(void) +test_item_modify(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) { - int ret; + int r; + char path[64], l_val[32]; + + TEST_START(ts_start); + + sprintf(path, "/perf:cont/lst[k1='%" PRIu32 "'][k2='str%" PRIu32 "']/l", state->count/2, state->count/2); + sprintf(l_val, "l%" PRIu32, 1); + if ((r = sr_set_item_str(state->sess, path, l_val, NULL, 0))) { + return r; + } + if ((r = sr_apply_changes(state->sess, 0))) { + return r; + } + + TEST_END(ts_end); + + return SR_ERR_OK; +} + +static int +test_item_remove(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end) +{ + int r; + char path[64], l_val[32]; + + TEST_START(ts_start); + + sprintf(path, "/perf:cont/lst[k1='%" PRIu32 "'][k2='str%" PRIu32 "']/l", state->count/2, state->count/2); + sprintf(l_val, "l%" PRIu32, 0); + if ((r = sr_delete_item(state->sess, path, 0))) { + return r; + } + if ((r = sr_apply_changes(state->sess, 0))) { + return r; + } + + TEST_END(ts_end); + + return SR_ERR_OK; +} + +static int +sysrepo_init(const char *plg_name, struct test_state *state, uint32_t count) +{ + int ret, i; sr_conn_ctx_t *conn; + sr_module_ds_t mod_ds; + + for (i = 0; i < 5; ++i) mod_ds.plugin_name[i] = plg_name; + mod_ds.plugin_name[5] = "JSON notif"; /* setup env */ if ((ret = setenv("SYSREPO_REPOSITORY_PATH", TESTS_REPO_DIR "/test_repositories/sr_perf", 1))) { @@ -605,15 +871,24 @@ sysrepo_init(void) sr_log_stderr(SR_LL_WRN); /* create connection */ - if ((ret = sr_connect(0, &conn))) { + if ((ret = sr_connect(SR_CONN_DEFAULT, &conn))) { return ret; } /* disable logging */ sr_log_stderr(SR_LL_NONE); + /* remove module if it was installed previously */ + ret = sr_remove_module(conn, "perf", 1); + if (ret && ret != SR_ERR_NOT_FOUND) { + return ret; + } + /* install module */ - ret = sr_install_module(conn, TESTS_SRC_DIR "/files/perf.yang", NULL, NULL); + ret = sr_install_module2(conn, TESTS_SRC_DIR "/files/perf.yang", NULL, NULL, &mod_ds, NULL, NULL, 0, NULL, NULL, LYD_XML); + if (ret) { + return ret; + } /* turn on logging */ sr_log_stderr(SR_LL_WRN); @@ -621,54 +896,219 @@ sysrepo_init(void) /* disconnect */ sr_disconnect(conn); - if (ret && (ret != SR_ERR_EXISTS)) { + state->count = count; + + return SR_ERR_OK; +} + +static int +sysrepo_destroy(void) +{ + int ret; + sr_conn_ctx_t *conn; + + /* create connection */ + if ((ret = sr_connect(0, &conn))) { return ret; } - /* turn on logging */ - sr_log_stderr(SR_LL_WRN); + /* remove module */ + ret = sr_remove_module(conn, "perf", 0); + + /* disconnect */ + sr_disconnect(conn); + + if (ret) { + return ret; + } return SR_ERR_OK; } +struct test tests[] = { + {"get tree", setup_running, test_get_tree, teardown_running}, + {"get item", setup_running, test_get_item, teardown_running}, + {"get tree hash", setup_running, test_get_tree_hash, teardown_running}, + {"get tree hash cached", setup_running_cached, test_get_tree_hash, teardown_running}, + {"get user ordered tree", setup_userordered_running, test_get_user_order_tree, teardown_running}, + {"get oper tree", setup_subscribe_oper, test_get_oper_tree, teardown_subscribe_oper}, + {"create batch", setup_empty, test_batch_create, teardown_empty}, + {"create user ordered items", setup_empty, test_user_order_items_create, teardown_empty}, + {"create all items", setup_empty, test_items_create, teardown_empty}, + {"create all items oper", setup_empty_oper, test_items_create_oper, teardown_empty}, + {"remove all items", setup_running, test_items_remove, teardown_empty}, + {"remove all items cached", setup_running_cached, test_items_remove, teardown_empty}, + {"create an item", setup_running, test_item_create, teardown_running}, + {"create an item cached", setup_running_cached, test_item_create, teardown_running}, + {"create an item oper", setup_oper, test_item_create_oper, teardown_oper}, + {"modify an item", setup_running, test_item_modify, teardown_running}, + {"modify an item cached", setup_running_cached, test_item_modify, teardown_running}, + {"remove an item", setup_running, test_item_remove, teardown_running}, + {"remove an item cached", setup_running_cached, test_item_remove, teardown_running}, +}; + +void +print_top_table_boundary(const char *plugin_name) +{ + uint32_t i; + const char *first = " test name ", *second = " time "; + const char *third = " comparison "; + + printf("\n %s\n", plugin_name); + + printf(" "); + for (i = 0; i < TABLE_WIDTH; ++i) printf("_"); + printf(" \n|"); + for (i = 0; i < TABLE_WIDTH; ++i) printf(" "); + + printf("|\n|"); + printf("%s", first); + for (i = strlen(first); i <= NAME_FIXED_LEN + 2; ++i) printf(" "); + printf("|"); + for (i = strlen(second); i < COL_FIXED_LEN - 1; ++i) printf(" "); + printf("%s", second); + printf("|"); + for (i = strlen(third); i < COL_FIXED_LEN; ++i) printf(" "); + printf("%s", third); + printf("|\n"); + + // top table boundary + printf("|"); + for (i = 0; i < TABLE_WIDTH; ++i) printf("_"); + printf("|\n"); + + printf("|"); + for (i = 0; i < TABLE_WIDTH; ++i) printf(" "); + + printf("|\n"); +} + +void +print_test_results(const char *test_name, int64_t time, int64_t dflt_time) +{ + uint32_t printed; + long double ratio = 1.0f; + uint64_t ratio_decimal; + + printf("| %s ", test_name); + printed = strlen(test_name); + while (printed < NAME_FIXED_LEN) { + printf("."); + printed += 1; + } + printf(" | %3" PRId64 ".%06" PRId64 " s |", time / MILLION, time % MILLION); + printf(" "); + + if (time < dflt_time) { + printf("\033[0;32;1m"); + ratio = ABS((long double)(dflt_time) / (long double)(time)); + } + else if (time > dflt_time) { + printf("\033[0;31;1m"); + ratio = ABS((long double)(time) / (long double)(dflt_time)); + } + ratio_decimal = (uint64_t)((ratio - (uint64_t)(ratio)) * 1000); + + printf("%7" PRIu64 ".%03" PRIu64 " x ", (uint64_t)(ratio), ratio_decimal); + printf("\033[0;37;1m"); + printf("|\n"); +} + +void +print_bottom_table_boundary() +{ + uint32_t i; + printf("|"); + for (i = 0; i < TABLE_WIDTH; ++i) printf("_"); + printf("|\n\n"); +} + int main(int argc, char **argv) { - int ret; - uint32_t i, count, tries; + int ret = 0; + uint32_t i, j, count, tries, plg_cnt, test_cnt; + const char *plg_name; + int64_t *times = NULL, time; + struct test_state state = {0}; + + /* change print color */ + printf("\033[0;37;1m"); + /* handle arguments */ if (argc < 3) { fprintf(stderr, "Usage:\n%s list-instance-count test-tries\n\n", argv[0]); return SR_ERR_INVAL_ARG; } count = atoi(argv[1]); - if (!count) { + if (count <= 0) { fprintf(stderr, "Invalid count \"%s\".\n", argv[1]); return SR_ERR_INVAL_ARG; } tries = atoi(argv[2]); - if (!tries) { + if (tries <= 0) { fprintf(stderr, "Invalid tries \"%s\".\n", argv[2]); return SR_ERR_INVAL_ARG; } - printf("\nsr_perf:\n\tdata set size: %" PRIu32 "\n\teach test executed: %" PRIu32 " %s\n\n", count, tries, - (tries > 1) ? "times" : "time"); + /* establish the number of plugins and tests */ + plg_cnt = sr_ds_plugin_int_count(); + test_cnt = (sizeof tests / sizeof(struct test)); - /* init */ - if ((ret = sysrepo_init())) { - return ret; + /* allocate a time var for every test of the default plugin */ + times = calloc(test_cnt, sizeof *times); + if (!times) { + fprintf(stderr, "Out of memory.\n"); + return SR_ERR_NO_MEMORY; } - /* tests */ - for (i = 0; i < (sizeof tests / sizeof(struct test)); ++i) { - if ((ret = exec_test(tests[i].setup, tests[i].test, tests[i].name, count, tries))) { - return ret; + printf("\n| Options\n\n Data set size : %" PRIu32 "\n Each test executed : %" PRIu32 " %s\n\n", count, tries, + (tries > 1) ? "times" : "time"); + printf("\n| Performance tests\n"); + + /* for every plugin run a set of tests */ + for (i = 0; i < plg_cnt; ++i) { + /* plugin name */ + plg_name = sr_internal_ds_plugins[i]->name; + + print_top_table_boundary(plg_name); + + /* init */ + if ((ret = sysrepo_init(plg_name, &state, count))) { + goto cleanup; + } + + /* tests */ + for (j = 0; j < test_cnt; ++j) { + if ((ret = exec_test(tests[j].setup, tests[j].test, tests[j].teardown, tries, &time, &state))) { + /* one of the tests failed */ + goto cleanup; + } + + /* store defaults plugin times to calculate the differences */ + if (i == 0) { + times[j] = time; + } + + print_test_results(tests[j].name, time, times[j]); + } + + /* destroy */ + if ((ret = sysrepo_destroy())) { + goto cleanup; } + + print_bottom_table_boundary(); } - printf("\n"); - return SR_ERR_OK; + printf("\nAll comparisons refer to how many times faster (green) or slower (red) the current plugin is compared to the first plugin.\n\n"); + + /* change print color */ + printf(" \033[0;37m"); + +cleanup: + free(times); + return ret; }