well,
it took me time to come back, lots of work these days,
On Tuesday 23 October 2012 19:28, Gustavo Sverzut Barbieri wrote :
> On Tue, Oct 23, 2012 at 7:30 PM, Jérémy Zurcher <[email protected]> wrote:
> >
> > Hi,
> >
> > I wanted to try to speed up eina_stringshare_del,
> > but I ended touching eina_stringshare_add first.
> >
> > p0.patch: _eina_share_common_find_hash(…)
> > merges _eina_share_common_cmp and calls to
> > eina_rbtree_inline_lookup
> >
> > p1.patch: eina_stringshare API
> > treat big strings first and avoid a call from
> > eina_stringshare_add
>
> p1 breaks del accounting of population, while I don't think it will
> make any difference:
>
> --- a/eina/src/lib/eina_stringshare.c
> +++ b/eina/src/lib/eina_stringshare.c
> @@ -582,28 +582,17 @@ eina_stringshare_del(Eina_Stringshare *str)
> return;
>
> /* special cases */
> - if (str[0] == '\0')
> - slen = 0;
> - else if (str[1] == '\0')
> - slen = 1;
> - else if (str[2] == '\0')
> - slen = 2;
> - else if (str[3] == '\0')
> - slen = 3;
> - else
> - slen = 4; /* handled later */
> -
> - if (slen < 2)
> + if (str[0] == '\0' || str[1] == '\0')
> return;
> - else if (slen < 4)
> + else if (str[2] == '\0' || str[3] == '\0')
> {
> + slen = (str[2] == '\0') ? 2 : 3;
> 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;
> }
>
> eina_share_common_population_del() will not run for sizes 0 and 1.
just like trunk does,
I can't break something that doet not work, hence the 3 attached patches.
there is no way to set EINA_SHARE_USAGE and EINA_SHARE_COMMON_USAGE
through configure but we can set EINA_STRINGSHARE_USAGE ... chose your patch
>
> There are some coding style issues and going over some abstraction
> such as accessing the RB tree in order to speed up.
>
I try my best to follow it.
going over the RB tree is the point, having to live with calls to
_eina_share_common_cmp is no good.
> From the images the speed up seems quite marginal and I wonder how you
> tested it. We provide the e17 benchmark that uses real-word strings,
> it's better than some random synthetic test. Did you run this? could
> you check with that?
I used the provided eina_bench_stringshare.c which focuses only on
eina_stringshare_add() whih is the path I was trying to speed up.
e17 bench brings undesired behaviours
- varying strings length which impact hash_superfast timing
- calls to eina_stringshare_ref and eina_stringshare_del
RB patch is not trying to find a real data best fitting algo,
but to shorten a given path which is data independant..
yes it is marginal, but still it's faster with my patch than running
lockless see the e17 benchmark numbers as you prefer them:
For `eina-trunk`:
# specimen experiment time starting time ending time
1 5138136 19947069256 19952207392
For `eina-trunk-lockless (lock calls commented)`:
# specimen experiment time starting time ending time
1 5108452 19934390545 19939498997
For `eina-RB-patched (with lock)`:
# specimen experiment time starting time ending time
1 5089091 19882278972 19887368063
will be even better with the other patch which serves long strings first,
that is real data targeting.
>
> note: AFAIR the biggest impact of the stringshare was the locks to
> make it multithread safe :-/
it is marginal, I've been told by benchmarks and gprof.
Cheers
Jérémy
>
> --
> Gustavo Sverzut Barbieri
> http://profusion.mobi embedded systems
> --------------------------------------
> MSN: [email protected]
> Skype: gsbarbieri
> Mobile: +55 (19) 9225-2202
>
> ------------------------------------------------------------------------------
> Everyone hates slow websites. So do we.
> Make your web apps faster with AppDynamics
> Download AppDynamics Lite for free today:
> http://p.sf.net/sfu/appdyn_sfd2d_oct
> _______________________________________________
> enlightenment-devel mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/enlightenment-devel
--
Jérémy Zurcher
av General Guisan 49
1400 Yverdon-les-bains
+41 (0) 79 599 84 27
diff --git a/eina/src/lib/eina_share_common.c b/eina/src/lib/eina_share_common.c
index 247fae7..523accc 100644
--- a/eina/src/lib/eina_share_common.c
+++ b/eina/src/lib/eina_share_common.c
@@ -119,6 +119,11 @@ static int _eina_share_common_count = 0;
#ifdef EINA_SHARE_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;
@@ -131,6 +136,7 @@ struct _Eina_Share
Eina_Magic node_magic;
#ifdef EINA_SHARE_COMMON_USAGE
Eina_Share_Common_Population population;
+ Eina_Share_Common_Population population_group[4];
int max_node_population;
#endif
};
@@ -173,22 +179,6 @@ 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 }
-};
-
static void
_eina_share_common_population_init(Eina_Share *share)
{
@@ -274,14 +264,14 @@ eina_share_common_population_del(Eina_Share *share, int
slen)
eina_lock_take(&_mutex_big);
share->population.count--;
- if (slen < 4)
+ if (slen < 4 )
share->population_group[slen].count--;
eina_lock_release(&_mutex_big);
}
static void
-_eina_share_common_population_head_init(Eina_Share *share,
+_eina_share_common_population_head_init(__UNUSED__ Eina_Share *share,
Eina_Share_Common_Head *head)
{
head->population = 1;
@@ -297,7 +287,7 @@ _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(__UNUSED__ Eina_Share *share,
Eina_Share_Common_Head *head)
{
head->population--;
@@ -926,8 +916,9 @@ eina_share_common_dump(Eina_Share *share, void
(*additional_dump)(
di.unique, di.dups, di.unique ? (di.dups * 100.0 / di.unique) : 0.0);
#ifdef EINA_SHARE_COMMON_USAGE
- printf("DDD: Allocated strings: %i\n", share->population.count);
- printf("DDD: Max allocated strings: %i\n", share->population.max);
+ 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) /
diff --git a/eina/src/lib/eina_stringshare.c b/eina/src/lib/eina_stringshare.c
index 2a457f4..85d7717 100644
--- a/eina/src/lib/eina_stringshare.c
+++ b/eina/src/lib/eina_stringshare.c
@@ -594,7 +594,10 @@ 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);
@@ -614,16 +617,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;
}
diff --git a/eina/src/lib/eina_share_common.c b/eina/src/lib/eina_share_common.c
index 91ad0db..c748473 100644
--- a/eina/src/lib/eina_share_common.c
+++ b/eina/src/lib/eina_share_common.c
@@ -117,8 +117,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;
@@ -129,8 +134,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
};
@@ -160,7 +166,7 @@ struct _Eina_Share_Common_Head
int hash;
-#ifdef EINA_SHARE_COMMON_USAGE
+#ifdef EINA_STRINGSHARE_USAGE
int population;
#endif
@@ -172,23 +178,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)
{
@@ -274,14 +264,14 @@ eina_share_common_population_del(Eina_Share *share, int
slen)
eina_lock_take(&_mutex_big);
share->population.count--;
- if (slen < 4)
+ if (slen < 4 )
share->population_group[slen].count--;
eina_lock_release(&_mutex_big);
}
static void
-_eina_share_common_population_head_init(Eina_Share *share,
+_eina_share_common_population_head_init(__UNUSED__ Eina_Share *share,
Eina_Share_Common_Head *head)
{
head->population = 1;
@@ -297,13 +287,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(__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(__UNUSED__ Eina_Share *share) {
}
@@ -915,7 +905,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;
@@ -926,9 +916,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
- printf("DDD: Allocated strings: %i\n", share->population.count);
- printf("DDD: Max allocated strings: %i\n", share->population.max);
+#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) /
diff --git a/eina/src/lib/eina_stringshare.c b/eina/src/lib/eina_stringshare.c
index 2a457f4..85d7717 100644
--- a/eina/src/lib/eina_stringshare.c
+++ b/eina/src/lib/eina_stringshare.c
@@ -594,7 +594,10 @@ 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);
@@ -614,16 +617,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;
}
diff --git a/eina/src/lib/eina_share_common.c b/eina/src/lib/eina_share_common.c
index 91ad0db..5d4f8c4 100644
--- a/eina/src/lib/eina_share_common.c
+++ b/eina/src/lib/eina_share_common.c
@@ -117,10 +117,6 @@ static int _eina_share_common_count = 0;
} \
} while (0)
-#ifdef EINA_SHARE_USAGE
-typedef struct _Eina_Share_Common_Population Eina_Share_Common_Population;
-#endif
-
typedef struct _Eina_Share_Common Eina_Share_Common;
typedef struct _Eina_Share_Common_Node Eina_Share_Common_Node;
typedef struct _Eina_Share_Common_Head Eina_Share_Common_Head;
@@ -129,10 +125,6 @@ struct _Eina_Share
{
Eina_Share_Common *share;
Eina_Magic node_magic;
-#ifdef EINA_SHARE_COMMON_USAGE
- Eina_Share_Common_Population population;
- int max_node_population;
-#endif
};
struct _Eina_Share_Common
@@ -160,10 +152,6 @@ struct _Eina_Share_Common_Head
int hash;
-#ifdef EINA_SHARE_COMMON_USAGE
- int population;
-#endif
-
Eina_Share_Common_Node *head;
Eina_Share_Common_Node builtin_node;
};
@@ -172,168 +160,6 @@ 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 }
-};
-
-static void
-_eina_share_common_population_init(Eina_Share *share)
-{
- unsigned int i;
-
- for (i = 0;
- i < sizeof (share->population_group) /
- sizeof (share->population_group[0]);
- ++i)
- {
- share->population_group[i].count = 0;
- share->population_group[i].max = 0;
- }
-}
-
-static void
-_eina_share_common_population_shutdown(Eina_Share *share)
-{
- unsigned int i;
-
- share->max_node_population = 0;
- share->population.count = 0;
- share->population.max = 0;
-
- for (i = 0;
- i < sizeof (share->population_group) /
- sizeof (share->population_group[0]);
- ++i)
- {
- share->population_group[i].count = 0;
- share->population_group[i].max = 0;
- }
-}
-
-static void
-_eina_share_common_population_stats(Eina_Share *share)
-{
- unsigned int i;
-
- fprintf(stderr, "eina share_common statistic:\n");
- fprintf(stderr,
- " * maximum shared strings : %i\n",
- share->population.max);
- fprintf(stderr,
- " * maximum shared strings per node : %i\n",
- share->max_node_population);
-
- for (i = 0;
- i < sizeof (share->population_group) /
- sizeof (share->population_group[0]);
- ++i)
- fprintf(stderr,
- "DDD: %i strings of length %u, max strings: %i\n",
- share->population_group[i].count,
- i,
- share->population_group[i].max);
-}
-
-void
-eina_share_common_population_add(Eina_Share *share, int slen)
-{
- eina_lock_take(&_mutex_big);
-
- share->population.count++;
- if (share->population.count > share->population.max)
- share->population.max = share->population.count;
-
- if (slen < 4)
- {
- share->population_group[slen].count++;
- if (share->population_group[slen].count >
- share->population_group[slen].max)
- share->population_group[slen].max =
- share->population_group[slen].count;
- }
-
- eina_lock_release(&_mutex_big);
-}
-
-void
-eina_share_common_population_del(Eina_Share *share, int slen)
-{
- eina_lock_take(&_mutex_big);
-
- share->population.count--;
- if (slen < 4)
- share->population_group[slen].count--;
-
- eina_lock_release(&_mutex_big);
-}
-
-static void
-_eina_share_common_population_head_init(Eina_Share *share,
- Eina_Share_Common_Head *head)
-{
- head->population = 1;
-}
-
-static void
-_eina_share_common_population_head_add(Eina_Share *share,
- Eina_Share_Common_Head *head)
-{
- head->population++;
- if (head->population > share->max_node_population)
- share->max_node_population = head->population;
-}
-
-static void
-_eina_share_common_population_head_del(Eina_Share *share,
- Eina_Share_Common_Head *head)
-{
- head->population--;
-}
-
-#else /* EINA_SHARE_COMMON_USAGE undefined */
-
-static void _eina_share_common_population_init(__UNUSED__ Eina_Share *share) {
-}
-static void _eina_share_common_population_shutdown(__UNUSED__ Eina_Share
*share)
-{
-}
-static void _eina_share_common_population_stats(__UNUSED__ Eina_Share *share) {
-}
-void eina_share_common_population_add(__UNUSED__ Eina_Share *share,
- __UNUSED__ int slen) {
-}
-void eina_share_common_population_del(__UNUSED__ Eina_Share *share,
- __UNUSED__ int slen) {
-}
-static void _eina_share_common_population_head_init(
- __UNUSED__ Eina_Share *share,
- __UNUSED__ Eina_Share_Common_Head *head) {
-}
-static void _eina_share_common_population_head_add(
- __UNUSED__ Eina_Share *share,
- __UNUSED__
- Eina_Share_Common_Head *head) {
-}
-static void _eina_share_common_population_head_del(
- __UNUSED__ Eina_Share *share,
- __UNUSED__
- Eina_Share_Common_Head *head) {
-}
-#endif
-
static int
_eina_share_common_cmp(const Eina_Share_Common_Head *ed,
const int *hash,
@@ -429,8 +255,6 @@ _eina_share_common_add_head(Eina_Share *share,
share->node_magic);
head->head->next = NULL;
- _eina_share_common_population_head_init(share, head);
-
*p_tree = eina_rbtree_inline_insert
(*p_tree, EINA_RBTREE_GET(head),
EINA_RBTREE_CMP_NODE_CB(_eina_share_common_node), NULL);
@@ -608,8 +432,6 @@ eina_share_common_init(Eina_Share **_share,
#undef EMS
EINA_MAGIC_SET(share->share, EINA_MAGIC_SHARE);
- _eina_share_common_population_init(share);
-
/* below is the common part among other all eina_share_common user */
if (_eina_share_common_count++ != 0)
return EINA_TRUE;
@@ -641,8 +463,6 @@ eina_share_common_shutdown(Eina_Share **_share)
eina_lock_take(&_mutex_big);
- _eina_share_common_population_stats(share);
-
/* remove any string still in the table */
for (i = 0; i < EINA_SHARE_COMMON_BUCKETS; i++)
{
@@ -654,8 +474,6 @@ eina_share_common_shutdown(Eina_Share **_share)
}
MAGIC_FREE(share->share);
- _eina_share_common_population_shutdown(share);
-
eina_lock_release(&_mutex_big);
free(*_share);
@@ -725,8 +543,6 @@ eina_share_common_add_length(Eina_Share *share,
if (!str)
return NULL;
- eina_share_common_population_add(share, slen);
-
if (slen <= 0)
return NULL;
@@ -773,7 +589,6 @@ eina_share_common_add_length(Eina_Share *share,
_eina_share_common_node_init(el, str, slen, null_size, share->node_magic);
el->next = ed->head;
ed->head = el;
- _eina_share_common_population_head_add(share, ed);
eina_lock_release(&_mutex_big);
@@ -799,8 +614,6 @@ eina_share_common_ref(Eina_Share *share, const char *str)
eina_lock_release(&_mutex_big);
- eina_share_common_population_add(share, node->length);
-
return str;
}
@@ -824,7 +637,6 @@ eina_share_common_del(Eina_Share *share, const char *str)
goto on_error;
slen = node->length;
- eina_share_common_population_del(share, slen);
if (node->references > 1)
{
node->references--;
@@ -853,8 +665,6 @@ eina_share_common_del(Eina_Share *share, const char *str)
if (!ed->head)
_eina_share_common_del_head(p_bucket, ed);
- else
- _eina_share_common_population_head_del(share, ed);
eina_lock_release(&_mutex_big);
@@ -915,32 +725,12 @@ eina_share_common_dump(Eina_Share *share, void
(*additional_dump)(
if (additional_dump)
additional_dump(&di);
-#ifdef EINA_SHARE_COMMON_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;
-#endif
printf("DDD:-------------------\n");
printf("DDD: usage (bytes) = %i, saved = %i (%3.0f%%)\n",
di.used, di.saved, di.used ? (di.saved * 100.0 / di.used) : 0.0);
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
- printf("DDD: Allocated strings: %i\n", share->population.count);
- printf("DDD: Max allocated strings: %i\n", share->population.max);
-
- for (i = 0;
- i < sizeof (share->population_group) /
- sizeof (share->population_group[0]);
- ++i)
- fprintf(stderr,
- "DDD: %i strings of length %u, max strings: %i\n",
- share->population_group[i].count,
- i,
- share->population_group[i].max);
-#endif
-
eina_lock_release(&_mutex_big);
}
diff --git a/eina/src/lib/eina_stringshare.c b/eina/src/lib/eina_stringshare.c
index 2a457f4..0df6cbc 100644
--- a/eina/src/lib/eina_stringshare.c
+++ b/eina/src/lib/eina_stringshare.c
@@ -597,7 +597,6 @@ eina_stringshare_del(Eina_Stringshare *str)
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);
@@ -743,15 +742,10 @@ eina_stringshare_ref(Eina_Stringshare *str)
slen = 3 + (int)strlen(str + 3);
if (slen < 2)
- {
- eina_share_common_population_add(stringshare_share, slen);
-
- return str;
- }
+ return str;
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);
------------------------------------------------------------------------------
WINDOWS 8 is here.
Millions of people. Your app in 30 days.
Visit The Windows 8 Center at Sourceforge for all your go to resources.
http://windows8center.sourceforge.net/
join-generation-app-and-make-money-coding-fast/
_______________________________________________
enlightenment-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/enlightenment-devel