eina: faster implementation of Eina_Rbtree by Alexandre Becoulet.
SVN revision: 68474
This commit is contained in:
parent
1a82751cf1
commit
411a4eb936
|
@ -225,3 +225,7 @@
|
||||||
2012-02-22 Cedric Bail
|
2012-02-22 Cedric Bail
|
||||||
|
|
||||||
* Add eina_file_stat.
|
* Add eina_file_stat.
|
||||||
|
|
||||||
|
2012-02-27 Alexandre Becoulet
|
||||||
|
|
||||||
|
* Add faster implementation of Eina_Rbtree.
|
||||||
|
|
|
@ -21,6 +21,9 @@ Fixes:
|
||||||
|
|
||||||
* compilation errors in Eina_RWLock code when building code on Windows > XP
|
* compilation errors in Eina_RWLock code when building code on Windows > XP
|
||||||
|
|
||||||
|
Improvements:
|
||||||
|
|
||||||
|
* faster implementation of Eina_Rbtree.
|
||||||
|
|
||||||
Eina 1.1.0 (2011-12-02)
|
Eina 1.1.0 (2011-12-02)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/* EINA - EFL data type library
|
/* EINA - EFL data type library
|
||||||
* Copyright (C) 2008 Cedric Bail
|
* Copyright (C) 2008 Cedric Bail
|
||||||
|
* Copyright (C) 2011 Alexandre Becoulet
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "eina_config.h"
|
#include "eina_config.h"
|
||||||
#include "eina_private.h"
|
#include "eina_private.h"
|
||||||
|
@ -244,9 +246,9 @@ static inline Eina_Rbtree *
|
||||||
_eina_rbtree_inline_single_rotation(Eina_Rbtree *node,
|
_eina_rbtree_inline_single_rotation(Eina_Rbtree *node,
|
||||||
Eina_Rbtree_Direction dir)
|
Eina_Rbtree_Direction dir)
|
||||||
{
|
{
|
||||||
Eina_Rbtree *save = node->son[!dir];
|
Eina_Rbtree *save = node->son[dir ^ 1];
|
||||||
|
|
||||||
node->son[!dir] = save->son[dir];
|
node->son[dir ^ 1] = save->son[dir];
|
||||||
save->son[dir] = node;
|
save->son[dir] = node;
|
||||||
|
|
||||||
node->color = EINA_RBTREE_RED;
|
node->color = EINA_RBTREE_RED;
|
||||||
|
@ -259,7 +261,7 @@ static inline Eina_Rbtree *
|
||||||
_eina_rbtree_inline_double_rotation(Eina_Rbtree *node,
|
_eina_rbtree_inline_double_rotation(Eina_Rbtree *node,
|
||||||
Eina_Rbtree_Direction dir)
|
Eina_Rbtree_Direction dir)
|
||||||
{
|
{
|
||||||
node->son[!dir] = _eina_rbtree_inline_single_rotation(node->son[!dir], !dir);
|
node->son[dir ^ 1] = _eina_rbtree_inline_single_rotation(node->son[dir ^ 1], dir ^ 1);
|
||||||
return _eina_rbtree_inline_single_rotation(node, dir);
|
return _eina_rbtree_inline_single_rotation(node, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,87 +279,64 @@ eina_rbtree_inline_insert(Eina_Rbtree *root,
|
||||||
Eina_Rbtree_Cmp_Node_Cb cmp,
|
Eina_Rbtree_Cmp_Node_Cb cmp,
|
||||||
const void *data)
|
const void *data)
|
||||||
{
|
{
|
||||||
Eina_Rbtree head;
|
Eina_Rbtree **r = &root;
|
||||||
Eina_Rbtree *g, *t; /* Grandparent & parent */
|
Eina_Rbtree *q = root;
|
||||||
Eina_Rbtree *p, *q; /* Iterator & parent */
|
uintptr_t stack[48];
|
||||||
/* WARNING:
|
unsigned int s = 0;
|
||||||
Compiler is not able to understand the underlying algorithm and don't know that
|
|
||||||
first top node is always black, so it will never use last before running the loop
|
|
||||||
one time.
|
|
||||||
*/
|
|
||||||
Eina_Rbtree_Direction dir, last;
|
|
||||||
|
|
||||||
EINA_SAFETY_ON_NULL_RETURN_VAL(node, root);
|
EINA_SAFETY_ON_NULL_RETURN_VAL(node, root);
|
||||||
EINA_SAFETY_ON_NULL_RETURN_VAL( cmp, root);
|
EINA_SAFETY_ON_NULL_RETURN_VAL( cmp, root);
|
||||||
|
|
||||||
if (!node)
|
/* Find insertion leaf */
|
||||||
return root;
|
while (q != NULL)
|
||||||
|
{
|
||||||
|
Eina_Rbtree_Direction dir = cmp(q, node, (void *)data);
|
||||||
|
|
||||||
|
/* Keep path in stack */
|
||||||
|
stack[s++] = (uintptr_t)r | dir;
|
||||||
|
|
||||||
|
r = q->son + dir;
|
||||||
|
q = *r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert */
|
||||||
|
*r = node;
|
||||||
_eina_rbtree_node_init(node);
|
_eina_rbtree_node_init(node);
|
||||||
|
|
||||||
if (!root)
|
/* Rebalance */
|
||||||
{
|
while (s > 0)
|
||||||
root = node;
|
{
|
||||||
goto end_add;
|
Eina_Rbtree *a, *b;
|
||||||
}
|
uintptr_t top = stack[--s]; /* Pop link pointer and direction */
|
||||||
|
Eina_Rbtree_Direction dir = top & 1;
|
||||||
|
|
||||||
memset(&head, 0, sizeof (Eina_Rbtree));
|
r = (Eina_Rbtree **)(top & ~1ULL);
|
||||||
last = dir = EINA_RBTREE_LEFT;
|
q = *r;
|
||||||
|
|
||||||
/* Set up helpers */
|
a = q->son[dir];
|
||||||
t = &head;
|
/* Rebalance done ? */
|
||||||
g = p = NULL;
|
if (a == NULL || a->color == EINA_RBTREE_BLACK)
|
||||||
q = t->son[1] = root;
|
break;
|
||||||
|
|
||||||
/* Search down the tree */
|
b = q->son[dir ^ 1];
|
||||||
for (;; )
|
if (b != NULL && b->color == EINA_RBTREE_RED)
|
||||||
{
|
{
|
||||||
if (!q)
|
q->color = EINA_RBTREE_RED;
|
||||||
/* Insert new node at the bottom */
|
b->color = a->color = EINA_RBTREE_BLACK;
|
||||||
p->son[dir] = q = node;
|
}
|
||||||
else if (_eina_rbtree_is_red(q->son[0])
|
else
|
||||||
&& _eina_rbtree_is_red(q->son[1]))
|
{
|
||||||
{
|
Eina_Rbtree *c = a->son[dir];
|
||||||
/* Color flip */
|
Eina_Rbtree *d = a->son[dir ^ 1];
|
||||||
q->color = EINA_RBTREE_RED;
|
|
||||||
q->son[0]->color = EINA_RBTREE_BLACK;
|
|
||||||
q->son[1]->color = EINA_RBTREE_BLACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fix red violation */
|
if (c != NULL && c->color == EINA_RBTREE_RED)
|
||||||
if (_eina_rbtree_is_red(q) && _eina_rbtree_is_red(p))
|
*r = _eina_rbtree_inline_single_rotation(*r, dir ^ 1);
|
||||||
{
|
else if (d != NULL && d->color == EINA_RBTREE_RED)
|
||||||
Eina_Rbtree_Direction dir2;
|
*r = _eina_rbtree_inline_double_rotation(*r, dir ^ 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dir2 = (t->son[1] == g) ? EINA_RBTREE_RIGHT : EINA_RBTREE_LEFT;
|
|
||||||
|
|
||||||
if (q == p->son[last])
|
|
||||||
t->son[dir2] = _eina_rbtree_inline_single_rotation(g, !last);
|
|
||||||
else
|
|
||||||
t->son[dir2] = _eina_rbtree_inline_double_rotation(g, !last);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop if found */
|
|
||||||
if (q == node)
|
|
||||||
break;
|
|
||||||
|
|
||||||
last = dir;
|
|
||||||
dir = cmp(q, node, (void *)data);
|
|
||||||
|
|
||||||
/* Update helpers */
|
|
||||||
if ( g )
|
|
||||||
t = g;
|
|
||||||
|
|
||||||
g = p, p = q;
|
|
||||||
q = q->son[dir];
|
|
||||||
}
|
|
||||||
|
|
||||||
root = head.son[1];
|
|
||||||
|
|
||||||
end_add:
|
|
||||||
/* Make root black */
|
|
||||||
root->color = EINA_RBTREE_BLACK;
|
root->color = EINA_RBTREE_BLACK;
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,122 +346,144 @@ eina_rbtree_inline_remove(Eina_Rbtree *root,
|
||||||
Eina_Rbtree_Cmp_Node_Cb cmp,
|
Eina_Rbtree_Cmp_Node_Cb cmp,
|
||||||
const void *data)
|
const void *data)
|
||||||
{
|
{
|
||||||
Eina_Rbtree head;
|
Eina_Rbtree *l0, *l1, *r, **rt = &root;
|
||||||
Eina_Rbtree *q, *p;
|
|
||||||
Eina_Rbtree *f = NULL;
|
|
||||||
Eina_Rbtree_Direction dir;
|
Eina_Rbtree_Direction dir;
|
||||||
|
uintptr_t stack[48];
|
||||||
|
unsigned int s = 0;
|
||||||
|
|
||||||
EINA_SAFETY_ON_NULL_RETURN_VAL(node, root);
|
EINA_SAFETY_ON_NULL_RETURN_VAL(node, root);
|
||||||
EINA_SAFETY_ON_NULL_RETURN_VAL( cmp, root);
|
EINA_SAFETY_ON_NULL_RETURN_VAL( cmp, root);
|
||||||
|
|
||||||
if (!root || !node)
|
/* Item search loop */
|
||||||
return root;
|
for (r = *rt; r != NULL; r = *rt)
|
||||||
|
{
|
||||||
|
if (r == node)
|
||||||
|
goto found;
|
||||||
|
|
||||||
memset(&head, 0, sizeof(Eina_Rbtree));
|
dir = cmp(r, node, (void*)data);
|
||||||
|
stack[s++] = (uintptr_t)rt | dir;
|
||||||
|
rt = r->son + dir;
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
|
||||||
dir = EINA_RBTREE_RIGHT;
|
found:
|
||||||
q = &head;
|
/* remove entry */
|
||||||
p = NULL;
|
l0 = node->son[0];
|
||||||
q->son[EINA_RBTREE_RIGHT] = root;
|
l1 = node->son[1];
|
||||||
|
|
||||||
/* Search and push a red down */
|
if (l0 != NULL && l1 != NULL) /* two links case */
|
||||||
while (q->son[dir])
|
{
|
||||||
{
|
Eina_Rbtree *q, **t, **p;
|
||||||
Eina_Rbtree_Direction last = dir;
|
uintptr_t ss;
|
||||||
Eina_Rbtree *g;
|
|
||||||
|
|
||||||
/* Update helpers */
|
stack[s++] = (uintptr_t)rt | 1;
|
||||||
g = p; p = q;
|
ss = s; /* keep predecessor right link stack index */
|
||||||
q = q->son[dir];
|
|
||||||
dir = cmp(q, node, (void *)data);
|
|
||||||
|
|
||||||
/* Save parent node found */
|
/* find predecessor */
|
||||||
if (q == node)
|
p = node->son + 1;
|
||||||
f = p;
|
q = *p;
|
||||||
|
|
||||||
/* Push the red node down */
|
while (1)
|
||||||
if (!_eina_rbtree_is_red(q)
|
{
|
||||||
&& !_eina_rbtree_is_red(q->son[dir]))
|
t = q->son;
|
||||||
{
|
q = *t;
|
||||||
if (_eina_rbtree_is_red(q->son[!dir]))
|
if (q == NULL)
|
||||||
q = p->son[last] = _eina_rbtree_inline_single_rotation(q, dir);
|
break;
|
||||||
else if (!_eina_rbtree_is_red(q->son[!dir]))
|
stack[s++] = (uintptr_t)p | 0;
|
||||||
{
|
p = t;
|
||||||
Eina_Rbtree *s = p->son[!last];
|
}
|
||||||
|
|
||||||
if (s)
|
/* detach predecessor */
|
||||||
{
|
q = *p;
|
||||||
if (!_eina_rbtree_is_red(s->son[EINA_RBTREE_LEFT])
|
*p = q->son[1];
|
||||||
&& !_eina_rbtree_is_red(s->son[EINA_RBTREE_RIGHT]))
|
|
||||||
{
|
|
||||||
/* Color flip */
|
|
||||||
p->color = EINA_RBTREE_BLACK;
|
|
||||||
p->son[EINA_RBTREE_LEFT]->color = EINA_RBTREE_RED;
|
|
||||||
p->son[EINA_RBTREE_RIGHT]->color = EINA_RBTREE_RED;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Eina_Rbtree_Direction dir2;
|
|
||||||
|
|
||||||
dir2 = g->son[1] ==
|
int c = q->color;
|
||||||
p ? EINA_RBTREE_RIGHT : EINA_RBTREE_LEFT;
|
|
||||||
|
|
||||||
if (_eina_rbtree_is_red(s->son[last]))
|
/* replace entry by predecessor */
|
||||||
{
|
memcpy(q, node, sizeof(Eina_Rbtree));
|
||||||
g->son[dir2] =
|
*rt = q;
|
||||||
_eina_rbtree_inline_double_rotation(p, last);
|
|
||||||
if (f == g)
|
|
||||||
{
|
|
||||||
p = g->son[dir2]->son[last];
|
|
||||||
f = g->son[dir2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (_eina_rbtree_is_red(s->son[!last]))
|
|
||||||
{
|
|
||||||
g->son[dir2] =
|
|
||||||
_eina_rbtree_inline_single_rotation(p, last);
|
|
||||||
if (f == g)
|
|
||||||
{
|
|
||||||
p = g->son[dir2]->son[last];
|
|
||||||
f = g->son[dir2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure correct coloring */
|
if (c == EINA_RBTREE_RED)
|
||||||
q->color = g->son[dir2]->color = EINA_RBTREE_RED;
|
goto end;
|
||||||
g->son[dir2]->son[EINA_RBTREE_LEFT]->color =
|
|
||||||
EINA_RBTREE_BLACK;
|
|
||||||
g->son[dir2]->son[EINA_RBTREE_RIGHT]->color =
|
|
||||||
EINA_RBTREE_BLACK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Replace and remove if found */
|
/* fix stack for replaced entry */
|
||||||
if (f)
|
if (s > ss)
|
||||||
{
|
stack[ss] = (uintptr_t)(q->son + 1) | 0;
|
||||||
/* 'q' should take the place of 'node' parent */
|
}
|
||||||
f->son[f->son[1] == node] = q;
|
else /* single link case */
|
||||||
|
{
|
||||||
|
if (l0 == NULL)
|
||||||
|
l0 = l1;
|
||||||
|
|
||||||
/* Switch the link from the parent to q's son */
|
*rt = l0;
|
||||||
p->son[p->son[1] == q] = q->son[!q->son[0]];
|
|
||||||
|
|
||||||
/* Put q at the place of node */
|
if (node->color == EINA_RBTREE_RED)
|
||||||
q->son[0] = node->son[0];
|
goto end; /* removed red */
|
||||||
q->son[1] = node->son[1];
|
|
||||||
q->color = node->color;
|
|
||||||
|
|
||||||
/* Reset node link */
|
if (l0 != NULL && l0->color == EINA_RBTREE_RED)
|
||||||
node->son[0] = NULL;
|
{
|
||||||
node->son[1] = NULL;
|
/* red child replace removed black */
|
||||||
}
|
l0->color = EINA_RBTREE_BLACK;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
root = head.son[1];
|
/* rebalance */
|
||||||
if (root)
|
while (s > 0)
|
||||||
|
{
|
||||||
|
Eina_Rbtree *q;
|
||||||
|
uintptr_t st = stack[--s];
|
||||||
|
|
||||||
|
rt = (Eina_Rbtree**)(st & ~1ULL);
|
||||||
|
dir = st & 1;
|
||||||
|
r = *rt;
|
||||||
|
q = r->son[dir ^ 1];
|
||||||
|
|
||||||
|
if (q != NULL && q->color == EINA_RBTREE_RED)
|
||||||
|
{
|
||||||
|
*rt = _eina_rbtree_inline_single_rotation(*rt, dir);
|
||||||
|
q = r->son[dir ^ 1];
|
||||||
|
rt = (*rt)->son + dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q != NULL)
|
||||||
|
{
|
||||||
|
int r_color = r->color;
|
||||||
|
Eina_Rbtree *nd = q->son[dir ^ 1];
|
||||||
|
|
||||||
|
if (nd != NULL && nd->color == EINA_RBTREE_RED)
|
||||||
|
{
|
||||||
|
*rt = _eina_rbtree_inline_single_rotation(*rt, dir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Eina_Rbtree *d = q->son[dir];
|
||||||
|
|
||||||
|
if (d != NULL && d->color == EINA_RBTREE_RED)
|
||||||
|
{
|
||||||
|
*rt = _eina_rbtree_inline_double_rotation(*rt, dir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r->color = EINA_RBTREE_BLACK;
|
||||||
|
q->color = EINA_RBTREE_RED;
|
||||||
|
if (r_color == EINA_RBTREE_RED)
|
||||||
|
break;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = *rt;
|
||||||
|
r->color = r_color;
|
||||||
|
r->son[1]->color = r->son[0]->color = EINA_RBTREE_BLACK;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (root != NULL)
|
||||||
root->color = EINA_RBTREE_BLACK;
|
root->color = EINA_RBTREE_BLACK;
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue