From: Jérémy Zurcher <jeremy@asynk.ch>

Subject: [E-devel] 2 steps eina_share_common_del speed up

builtin node is never unlinked even if empty, always is the last of the queue,
so that it can be used to get a pointer to head.
      
cost: never unlink or promote builtin node.
benefit: no need to hash and search rbtree to unlink an empty node,
only to remove an empty head.

store full hash in Eina_Share_Common_Head, so we only hash once
use 8 lower bits as node hash, use next 8 bits as bucket index.
    
cost: have to apply 0xFF mask on hash in rbtree callbacks.
benefit: no need to hash when removing an empty head.
        


SVN revision: 82161
This commit is contained in:
Jérémy Zurcher 2013-01-04 08:41:47 +00:00 committed by Carsten Haitzler
parent 9b252e40d4
commit 1424ac7d4d
4 changed files with 80 additions and 53 deletions

View File

@ -1,3 +1,7 @@
2013-01-04 Jérémy Zurcher
* Improve eina_share string del speed by a maybe 5-15%.
2013-01-03 Gustavo Sverzut Barbieri (k-s)
* Add eina_alloc.h to Eina.h to define alloca()

1
NEWS
View File

@ -73,6 +73,7 @@ Improvements:
* all efl object-freeing functions now take NULL without crashing or erroring
* use Eina_File in webp, gif, tiff, png and eet loader
* Eina.h includes eina_alloca.h/alloca.h to define alloca()
* Improved eina share del speed.
Fixes:
* Fix PPC (big endian) image codec bug.

View File

@ -88,6 +88,8 @@
#define EINA_SHARE_COMMON_BUCKETS 256
#define EINA_SHARE_COMMON_MASK 0xFF
#define EINA_SHARE_COMMON_BUCKET_IDX(h) ((h >> 8) & EINA_SHARE_COMMON_MASK)
#define EINA_SHARE_COMMON_NODE_HASH(h) (h & EINA_SHARE_COMMON_MASK)
static const char EINA_MAGIC_SHARE_STR[] = "Eina Share";
static const char EINA_MAGIC_SHARE_HEAD_STR[] = "Eina Share Head";
@ -113,8 +115,13 @@ static int _eina_share_common_count = 0;
} \
} while (0)
#ifdef EINA_SHARE_USAGE
#ifdef EINA_STRINGSHARE_USAGE
typedef struct _Eina_Share_Common_Population Eina_Share_Common_Population;
struct _Eina_Share_Common_Population
{
int count;
int max;
};
#endif
typedef struct _Eina_Share_Common Eina_Share_Common;
@ -125,8 +132,9 @@ struct _Eina_Share
{
Eina_Share_Common *share;
Eina_Magic node_magic;
#ifdef EINA_SHARE_COMMON_USAGE
#ifdef EINA_STRINGSHARE_USAGE
Eina_Share_Common_Population population;
Eina_Share_Common_Population population_group[4];
int max_node_population;
#endif
};
@ -156,7 +164,7 @@ struct _Eina_Share_Common_Head
int hash;
#ifdef EINA_SHARE_COMMON_USAGE
#ifdef EINA_STRINGSHARE_USAGE
int population;
#endif
@ -168,22 +176,7 @@ Eina_Bool _share_common_threads_activated = EINA_FALSE;
static Eina_Lock _mutex_big;
#ifdef EINA_SHARE_COMMON_USAGE
struct _Eina_Share_Common_Population
{
int count;
int max;
};
static Eina_Share_Common_Population population = { 0, 0 };
static Eina_Share_Common_Population population_group[4] =
{
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 }
};
#ifdef EINA_STRINGSHARE_USAGE
static void
_eina_share_common_population_init(Eina_Share *share)
@ -277,7 +270,7 @@ eina_share_common_population_del(Eina_Share *share, int slen)
}
static void
_eina_share_common_population_head_init(Eina_Share *share,
_eina_share_common_population_head_init(EINA_UNUSED Eina_Share *share,
Eina_Share_Common_Head *head)
{
head->population = 1;
@ -293,13 +286,13 @@ _eina_share_common_population_head_add(Eina_Share *share,
}
static void
_eina_share_common_population_head_del(Eina_Share *share,
_eina_share_common_population_head_del(EINA_UNUSED Eina_Share *share,
Eina_Share_Common_Head *head)
{
head->population--;
}
#else /* EINA_SHARE_COMMON_USAGE undefined */
#else /* EINA_STRINGSHARE_USAGE undefined */
static void _eina_share_common_population_init(EINA_UNUSED Eina_Share *share) {
}
@ -338,7 +331,7 @@ _eina_share_common_cmp(const Eina_Share_Common_Head *ed,
{
EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(ed, , 0);
return ed->hash - *hash;
return EINA_SHARE_COMMON_NODE_HASH(ed->hash) - *hash;
}
static Eina_Rbtree_Direction
@ -349,7 +342,7 @@ _eina_share_common_node(const Eina_Share_Common_Head *left,
EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(left, , 0);
EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(right, , 0);
if (left->hash - right->hash < 0)
if (EINA_SHARE_COMMON_NODE_HASH(left->hash) - EINA_SHARE_COMMON_NODE_HASH(right->hash) < 0)
return EINA_RBTREE_LEFT;
return EINA_RBTREE_RIGHT;
@ -473,10 +466,13 @@ _eina_share_common_head_find(Eina_Share_Common_Head *head,
for (; node; prev = node, node = node->next)
if (_eina_share_common_node_eq(node, str, slen))
{
/* promote node, make hot items be at the beginning */
prev->next = node->next;
node->next = head->head;
head->head = node;
/* promote node except builtin_node, make hot items be at the beginning */
if (node->next)
{
prev->next = node->next;
node->next = head->head;
head->head = node;
}
return node;
}
@ -515,6 +511,20 @@ _eina_share_common_find_hash(Eina_Share_Common_Head *bucket, int hash)
EINA_RBTREE_CMP_KEY_CB(_eina_share_common_cmp), NULL);
}
static Eina_Share_Common_Head *
_eina_share_common_head_from_node(Eina_Share_Common_Node *node)
{
Eina_Share_Common_Head *head;
const size_t offset = offsetof(Eina_Share_Common_Head, builtin_node);
while (node->next)
node = node->next;
head = (Eina_Share_Common_Head *)((char*)node - offset);
EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(head, , 0);
return head;
}
static Eina_Share_Common_Node *
_eina_share_common_node_alloc(unsigned int slen, unsigned int null_size)
{
@ -716,7 +726,7 @@ eina_share_common_add_length(Eina_Share *share,
{
Eina_Share_Common_Head **p_bucket, *ed;
Eina_Share_Common_Node *el;
int hash_num, hash;
int hash;
if (!str)
return NULL;
@ -727,13 +737,11 @@ eina_share_common_add_length(Eina_Share *share,
return NULL;
hash = eina_hash_superfast(str, slen);
hash_num = hash & 0xFF;
hash = (hash >> 8) & EINA_SHARE_COMMON_MASK;
eina_lock_take(&_mutex_big);
p_bucket = share->share->buckets + hash_num;
p_bucket = share->share->buckets + EINA_SHARE_COMMON_BUCKET_IDX(hash);
ed = _eina_share_common_find_hash(*p_bucket, hash);
ed = _eina_share_common_find_hash(*p_bucket, EINA_SHARE_COMMON_NODE_HASH(hash));
if (!ed)
{
const char *s = _eina_share_common_add_head(share,
@ -808,7 +816,6 @@ eina_share_common_del(Eina_Share *share, const char *str)
Eina_Share_Common_Head *ed;
Eina_Share_Common_Head **p_bucket;
Eina_Share_Common_Node *node;
int hash_num, hash;
if (!str)
return EINA_TRUE;
@ -830,25 +837,24 @@ eina_share_common_del(Eina_Share *share, const char *str)
node->references = 0;
hash = eina_hash_superfast(str, slen);
hash_num = hash & 0xFF;
hash = (hash >> 8) & EINA_SHARE_COMMON_MASK;
p_bucket = share->share->buckets + hash_num;
ed = _eina_share_common_find_hash(*p_bucket, hash);
ed = _eina_share_common_head_from_node(node);
if (!ed)
goto on_error;
EINA_MAGIC_CHECK_SHARE_COMMON_HEAD(ed, eina_lock_release(&_mutex_big), EINA_FALSE);
if (!_eina_share_common_head_remove_node(ed, node))
goto on_error;
if (node != &ed->builtin_node)
MAGIC_FREE(node);
{
if (!_eina_share_common_head_remove_node(ed, node))
goto on_error;
MAGIC_FREE(node);
}
if (!ed->head)
_eina_share_common_del_head(p_bucket, ed);
if (!ed->head || ed->head->references == 0)
{
p_bucket = share->share->buckets + EINA_SHARE_COMMON_BUCKET_IDX(ed->hash);
_eina_share_common_del_head(p_bucket, ed);
}
else
_eina_share_common_population_head_del(share, ed);
@ -911,7 +917,7 @@ eina_share_common_dump(Eina_Share *share, void (*additional_dump)(
if (additional_dump)
additional_dump(&di);
#ifdef EINA_SHARE_COMMON_USAGE
#ifdef EINA_STRINGSHARE_USAGE
/* One character strings are not counted in the hash. */
di.saved += share->population_group[0].count * sizeof(char);
di.saved += share->population_group[1].count * sizeof(char) * 2;
@ -922,9 +928,10 @@ eina_share_common_dump(Eina_Share *share, void (*additional_dump)(
printf("DDD: unique: %d, duplicates: %d (%3.0f%%)\n",
di.unique, di.dups, di.unique ? (di.dups * 100.0 / di.unique) : 0.0);
#ifdef EINA_SHARE_COMMON_USAGE
#ifdef EINA_STRINGSHARE_USAGE
printf("DDD: Allocated strings: %i\n", share->population.count);
printf("DDD: Max allocated strings: %i\n", share->population.max);
printf("DDD: Max shared strings per node : %i\n", share->max_node_population);
for (i = 0;
i < sizeof (share->population_group) /

View File

@ -578,13 +578,18 @@ eina_stringshare_del(Eina_Stringshare *str)
slen = 4; /* handled later */
if (slen < 2)
return;
{
eina_share_common_population_del(stringshare_share, slen);
return;
}
else if (slen < 4)
{
eina_share_common_population_del(stringshare_share, slen);
eina_lock_take(&_mutex_small);
_eina_stringshare_small_del(str, slen);
eina_lock_release(&_mutex_small);
return;
}
@ -598,16 +603,26 @@ eina_stringshare_add_length(const char *str, unsigned int slen)
if (!str)
return NULL;
else if (slen == 0)
return "";
{
eina_share_common_population_add(stringshare_share, slen);
return "";
}
else if (slen == 1)
return (Eina_Stringshare *) _eina_stringshare_single + ((*str) << 1);
{
eina_share_common_population_add(stringshare_share, slen);
return (Eina_Stringshare *) _eina_stringshare_single + ((*str) << 1);
}
else if (slen < 4)
{
const char *s;
eina_share_common_population_add(stringshare_share, slen);
eina_lock_take(&_mutex_small);
s = _eina_stringshare_small_add(str, slen);
eina_lock_release(&_mutex_small);
return s;
}
@ -712,7 +727,7 @@ eina_stringshare_ref(Eina_Stringshare *str)
int slen;
if (!str)
return eina_share_common_ref(stringshare_share, str);
return NULL;
/* special cases */
if (str[0] == '\0')
@ -735,8 +750,8 @@ eina_stringshare_ref(Eina_Stringshare *str)
else if (slen < 4)
{
const char *s;
eina_share_common_population_add(stringshare_share, slen);
eina_share_common_population_add(stringshare_share, slen);
eina_lock_take(&_mutex_small);
s = _eina_stringshare_small_add(str, slen);
eina_lock_release(&_mutex_small);