Skip to content

Commit

Permalink
tree data OPTIMIZE of lyd_dup() for (leaf-)list
Browse files Browse the repository at this point in the history
Faster node insertion. There is no need to search for the 'anchor'
for instances of the (leaf-)list, thus reducing the number of accesses
to the hash table.
  • Loading branch information
lePici committed Feb 13, 2024
1 parent b5189da commit e7ce2ab
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 9 deletions.
82 changes: 73 additions & 9 deletions src/tree_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -2177,6 +2177,58 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_
return ret;
}

/**
* @brief Duplicate a (leaf-)list and connect it into @p parent (if present) or last of @p first siblings.
*
* @param[in] orig Node to duplicate.
* @param[in] trg_ctx Target context for duplicated nodes.
* @param[in] parent Parent to insert into, NULL for top-level sibling.
* @param[in,out] first First sibling, NULL if no top-level sibling exist yet. Can be also NULL if @p parent is set.
* @param[in] options Bitmask of options flags, see @ref dupoptions.
* @param[out] dup_p Pointer where the created duplicated node is placed (besides connecting it to @p parent / @p first).
* @return LY_ERR value.
*/
static LY_ERR
lyd_dup_list(const struct lyd_node **orig, const struct ly_ctx *trg_ctx, struct lyd_node *parent,
struct lyd_node **first, uint32_t options, struct lyd_node **dup_p)
{
LY_ERR rc;
struct lyd_node *start, *leader, *dup;
const struct lysc_node *schema;
uint32_t insert_order;

/* duplicate leader */
start = (*orig)->next;
schema = (*orig)->schema;
rc = lyd_dup_r(*orig, trg_ctx, parent, LYD_INSERT_NODE_DEFAULT, first, options, &leader);
LY_CHECK_RET(rc);

if (!start || !start->schema || !LYD_NODE_IS_ALONE(leader)) {
/* no other instances */
if (dup_p) {
*dup_p = leader;
}
return LY_SUCCESS;
}

/* duplicate the rest of the nodes in the (leaf-)list */
insert_order = leader->next ? LYD_INSERT_NODE_LAST_BY_SCHEMA : LYD_INSERT_NODE_LAST;
LY_LIST_FOR(start, *orig) {
if (schema != (*orig)->schema) {
break;
}
rc = lyd_dup_r(*orig, trg_ctx, parent, insert_order, first, options, &dup);
LY_CHECK_GOTO(rc, cleanup);
}

cleanup:
if (dup_p) {
*dup_p = leader;
}

return rc;
}

/**
* @brief Get a parent node to connect duplicated subtree to.
*
Expand Down Expand Up @@ -2255,13 +2307,15 @@ lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_c

static LY_ERR
lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent, uint32_t options,
ly_bool nosiblings, struct lyd_node **dup)
ly_bool nosiblings, struct lyd_node **dup_p)
{
LY_ERR rc;
const struct lyd_node *orig; /* original node to be duplicated */
struct lyd_node *first = NULL; /* the first duplicated node, this is returned */
struct lyd_node *first_dup = NULL; /* the first duplicated node, this is returned */
struct lyd_node *top = NULL; /* the most higher created node */
struct lyd_node *local_parent = NULL; /* the direct parent node for the duplicated node(s) */
struct lyd_node *dup; /* duplicate node */
struct lyd_node *first_sibling = NULL; /* first sibling node */

assert(node && trg_ctx);

Expand All @@ -2276,36 +2330,46 @@ lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_no
if (lysc_is_key(orig->schema)) {
if (local_parent) {
/* the key must already exist in the parent */
rc = lyd_find_sibling_schema(lyd_child(local_parent), orig->schema, first ? NULL : &first);
rc = lyd_find_sibling_schema(lyd_child(local_parent), orig->schema, &dup);
LY_CHECK_ERR_GOTO(rc, LOGINT(trg_ctx), error);
} else {
assert(!(options & LYD_DUP_WITH_PARENTS));
/* duplicating a single key, okay, I suppose... */
rc = lyd_dup_r(orig, trg_ctx, NULL, LYD_INSERT_NODE_DEFAULT, &first, options, first ? NULL : &first);
rc = lyd_dup_r(orig, trg_ctx, NULL, LYD_INSERT_NODE_DEFAULT, &first_sibling, options, &dup);
LY_CHECK_GOTO(rc, error);
}
} else if (!nosiblings && orig->schema && (orig->schema->nodetype & (LYS_LIST | LYS_LEAFLIST))) {
/* duplicate the whole (leaf-)list */
rc = lyd_dup_list(&orig, trg_ctx, local_parent, &first_sibling, options, &dup);
LY_CHECK_GOTO(rc, error);
if (!orig) {
break;
}
} else {
/* if there is no local parent, it will be inserted into first */
rc = lyd_dup_r(orig, trg_ctx, local_parent,
options & LYD_DUP_NO_LYDS ? LYD_INSERT_NODE_LAST_BY_SCHEMA : LYD_INSERT_NODE_DEFAULT,
&first, options, first ? NULL : &first);
&first_sibling, options, &dup);
LY_CHECK_GOTO(rc, error);
}
first_dup = first_dup ? first_dup : dup;

if (nosiblings) {
break;
}
}

if (dup) {
*dup = first;
if (dup_p) {
*dup_p = first_dup;
}
return LY_SUCCESS;

error:
if (top) {
lyd_free_tree(top);
} else if (first_dup) {
lyd_free_siblings(first_dup);
} else {
lyd_free_siblings(first);
lyd_free_siblings(dup);
}
return rc;
}
Expand Down
35 changes: 35 additions & 0 deletions tests/perf/perf.c
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,24 @@ setup_data_no_same_trees(const struct lys_module *mod, uint32_t count, struct te
return LY_SUCCESS;
}

static LY_ERR
setup_data_empty_and_full_trees(const struct lys_module *mod, uint32_t count, struct test_state *state)
{
LY_ERR ret;

state->mod = mod;
state->count = count;

if ((ret = create_list_inst(mod, 0, 0, &state->data1))) {
return ret;
}
if ((ret = create_list_inst(mod, 0, count, &state->data2))) {
return ret;
}

return LY_SUCCESS;
}

static LY_ERR
setup_data_offset_tree(const struct lys_module *mod, uint32_t count, struct test_state *state)
{
Expand Down Expand Up @@ -549,6 +567,22 @@ test_dup(struct test_state *state, struct timespec *ts_start, struct timespec *t
return LY_SUCCESS;
}

static LY_ERR
test_dup_siblings_to_empty(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
{
LY_ERR r;

TEST_START(ts_start);

if ((r = lyd_dup_siblings(lyd_child(state->data2), (struct lyd_node_inner *)state->data1, 0, NULL))) {
return r;
}

TEST_END(ts_end);

return LY_SUCCESS;
}

static LY_ERR
test_free(struct test_state *state, struct timespec *ts_start, struct timespec *ts_end)
{
Expand Down Expand Up @@ -749,6 +783,7 @@ struct test tests[] = {
{"print json", setup_data_single_tree, test_print_json},
{"print lyb", setup_data_single_tree, test_print_lyb},
{"dup", setup_data_single_tree, test_dup},
{"dup_siblings_to_empty", setup_data_empty_and_full_trees, test_dup_siblings_to_empty},
{"free", setup_basic, test_free},
{"xpath find", setup_data_single_tree, test_xpath_find},
{"xpath find hash", setup_data_single_tree, test_xpath_find_hash},
Expand Down

0 comments on commit e7ce2ab

Please sign in to comment.