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 sheet, thus reducing the number of accesses
to the hash table.
  • Loading branch information
lePici committed Feb 9, 2024
1 parent 368d3a9 commit 40c9cc4
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 21 deletions.
108 changes: 87 additions & 21 deletions src/tree_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -2003,19 +2003,17 @@ lyd_find_schema_ctx(const struct lysc_node *schema, const struct ly_ctx *trg_ctx
* @param[in] node 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] insert_last Whether the duplicated node can be inserted as the last child of @p parent. Set for
* recursive duplication as an optimization.
* @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_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent, ly_bool insert_last,
struct lyd_node **first, uint32_t options, struct lyd_node **dup_p)
lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent, uint32_t options,
struct lyd_node **dup_p)
{
LY_ERR ret;
struct lyd_node *dup = NULL;
struct lyd_node *dup = NULL, *dup_child;
struct lyd_meta *meta;
struct lyd_attr *attr;
struct lyd_node_any *any;
Expand Down Expand Up @@ -2105,7 +2103,8 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_
if (options & LYD_DUP_RECURSIVE) {
/* duplicate all the children */
LY_LIST_FOR(orig->child, child) {
LY_CHECK_GOTO(ret = lyd_dup_r(child, trg_ctx, dup, 1, NULL, options, NULL), error);
LY_CHECK_GOTO(ret = lyd_dup_r(child, trg_ctx, dup, options, &dup_child), error);
lyd_insert_node(dup, NULL, dup_child, 1);
}
}
LY_CHECK_GOTO(ret = lydict_insert(trg_ctx, orig->name.name, 0, &opaq->name.name), error);
Expand Down Expand Up @@ -2141,12 +2140,14 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_
if (options & LYD_DUP_RECURSIVE) {
/* duplicate all the children */
LY_LIST_FOR(orig->child, child) {
LY_CHECK_GOTO(ret = lyd_dup_r(child, trg_ctx, dup, 1, NULL, options, NULL), error);
LY_CHECK_GOTO(ret = lyd_dup_r(child, trg_ctx, dup, options, &dup_child), error);
lyd_insert_node(dup, NULL, dup_child, 1);
}
} else if ((dup->schema->nodetype == LYS_LIST) && !(dup->schema->flags & LYS_KEYLESS)) {
/* always duplicate keys of a list */
for (child = orig->child; child && lysc_is_key(child->schema); child = child->next) {
LY_CHECK_GOTO(ret = lyd_dup_r(child, trg_ctx, dup, 1, NULL, options, NULL), error);
LY_CHECK_GOTO(ret = lyd_dup_r(child, trg_ctx, dup, options, &dup_child), error);
lyd_insert_node(dup, NULL, dup_child, 1);
}
}
lyd_hash(dup);
Expand All @@ -2156,9 +2157,6 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_
LY_CHECK_GOTO(ret = lyd_any_copy_value(dup, &any->value, any->value_type), error);
}

/* insert */
lyd_insert_node(parent, first, dup, insert_last);

if (dup_p) {
*dup_p = dup;
}
Expand All @@ -2169,6 +2167,60 @@ 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, *last;
const struct lysc_node *schema;

/* duplicate leader */
start = (*orig)->next;
schema = (*orig)->schema;
rc = lyd_dup_r(*orig, trg_ctx, parent, options, &leader);
lyd_insert_node(parent, first, leader, 0);
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 */
last = leader;
LY_LIST_FOR(start, *orig) {
if (schema != (*orig)->schema) {
break;
}
rc = lyd_dup_r(*orig, trg_ctx, parent, options, &dup);
LY_CHECK_GOTO(rc, cleanup);
lyd_insert_after_node(first, last, dup);
last = dup;
}

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

return rc;
}

/**
* @brief Get a parent node to connect duplicated subtree to.
*
Expand Down Expand Up @@ -2210,7 +2262,7 @@ lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_c
repeat = 0;
} else {
iter = NULL;
LY_CHECK_RET(lyd_dup_r(orig_parent, trg_ctx, NULL, 0, &iter, options, &iter));
LY_CHECK_RET(lyd_dup_r(orig_parent, trg_ctx, NULL, options, &iter));

/* insert into the previous duplicated parent */
if (*dup_parent) {
Expand Down Expand Up @@ -2247,13 +2299,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 @@ -2268,34 +2322,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, 0, &first, options, first ? NULL : &first);
rc = lyd_dup_r(orig, trg_ctx, NULL, options, &dup);
lyd_insert_node(NULL, &first_sibling, dup, 0);
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, 0, &first, options, first ? NULL : &first);
rc = lyd_dup_r(orig, trg_ctx, local_parent, options, &dup);
lyd_insert_node(local_parent, &first_sibling, dup, 0);
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(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", setup_data_empty_and_full_trees, test_dup_siblings},
{"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 40c9cc4

Please sign in to comment.