On 17/08/16 00:17, Carsten Haitzler (The Rasterman) wrote: > On Tue, 16 Aug 2016 16:36:20 +0100 Tom Hacohen <t...@osg.samsung.com> said: > >> This change means you can abuse efl_object_override() a bit more and >> rely on it in API. Because while the save is significant for normal >> classes, it would be even more significant for object override because >> the amount of shared vtables will be higher (most likely, as the number >> of overrides tends to be smaller and more clustered). > > shouldn't this be efl_override() now anyway? ... why object? of course its an > object... every eo api works on an object... or maybe it should be > efl_func_override or efl_method_override() ... :)
I changed some of the APIs to "efl_object_" instead of the "efl_" because I felt they really felt ambiguous and not descriptive enough. I'm happy with changing this to whatever people want. I prefer object_override than the suggestions above though I don't really care. > >> -- >> Tom. >> >> >> On 16/08/16 16:34, Tom Hacohen wrote: >>> tasn pushed a commit to branch master. >>> >>> http://git.enlightenment.org/core/efl.git/commit/?id=28c80f91221ae2639f4573046d8621a2a4d18cda >>> >>> commit 28c80f91221ae2639f4573046d8621a2a4d18cda >>> Author: Tom Hacohen <t...@stosb.com> >>> Date: Mon Aug 15 17:11:13 2016 +0100 >>> >>> Efl object: implement CoW for the function vtables >>> >>> This commit implements a sort of CoW for the vtables. The vtables are >>> usually just linked to and refcounted. When we need to change them we >>> allocate new ones and copy them over so we can write to them. >>> >>> I wrote some code to measure the effectiveness of this change. When >>> running elementary_test (and immediately exiting) I saw that out of the >>> total number of vtable chains (561) that were needed by the classes in >>> the EFL, 79 (14.08%) were reused. Considering that I had to add >>> refcounting (unsigned short, but let's consider it's the size of a word >>> because of alignment), I would calculate the saving as such (in bytes): >>> >>> Number of items in a chain (refcounted block): 32 >>> >>> 32 bit: >>> sizeof(chain_node) = 8 >>> Mem wasted on refcounting: 561 * 4 = 2244 >>> Mem saved because of sharing: 79 * (32 * 8) = 20224 >>> Total save: 17980 bytes >>> >>> 64 bit: >>> sizeof(chain_node) = 16 >>> Mem wasted on refcounting: 561 * 8 = 4488 >>> Mem saved because of sharing: 79 * (32 * 16) = 40448 >>> Total save: 35960 bytes >>> >>> Wow, we use a lot of memory in Eo classes, I'm sure we can >>> save even more if we put our hearts into it (change the shareable units >>> to be smaller to increase the chance of sharing). >>> This is internal and doesn't affect API/ABI so we can change this even >>> further with time. >>> >>> This also improves efl_object_override(). This should now be quite >>> memory efficient (don't abuse, but it's not a big hogg as it was), so >>> feel free to abuse that one and rely on it in API. >>> >>> @feature >>> --- >>> src/lib/eo/eo.c | 114 +++++++++++++++++++++++++++++++++++ >>> +------------ src/lib/eo/eo_private.h | 14 +++++- >>> 2 files changed, 98 insertions(+), 30 deletions(-) >>> >>> diff --git a/src/lib/eo/eo.c b/src/lib/eo/eo.c >>> index 9104a9e..0f530b4 100644 >>> --- a/src/lib/eo/eo.c >>> +++ b/src/lib/eo/eo.c >>> @@ -41,12 +41,6 @@ static inline void _efl_data_xunref_internal(_Eo_Object >>> *obj, void *data, const >>> >>> /* Start of Dich */ >>> >>> -/* How we search and store the implementations in classes. */ >>> -#define DICH_CHAIN_LAST_BITS 5 >>> -#define DICH_CHAIN_LAST_SIZE (1 << DICH_CHAIN_LAST_BITS) >>> -#define DICH_CHAIN1(x) ((x) >> DICH_CHAIN_LAST_BITS) >>> -#define DICH_CHAIN_LAST(x) ((x) & ((1 << DICH_CHAIN_LAST_BITS) - 1)) >>> - >>> >>> /* We are substracting the mask here instead of "AND"ing because it's a >>> hot path, >>> * it should be a valid class at this point, and this lets the compiler do >>> 1 @@ -58,11 +52,86 @@ static inline void _efl_data_xunref_internal >>> (_Eo_Object *obj, void *data, const }) >>> >>> static inline void >>> +_vtable_chain2_unref(Dich_Chain2 *chain) >>> +{ >>> + if (--(chain->refcount) == 0) >>> + { >>> + free(chain); >>> + } >>> +} >>> + >>> +static inline void >>> _vtable_chain_alloc(Dich_Chain1 *chain1) >>> { >>> - if (!chain1->funcs) >>> + chain1->chain2 = calloc(1, sizeof(*(chain1->chain2))); >>> + chain1->chain2->refcount = 1; >>> +} >>> + >>> +static inline void _vtable_chain_write_prepare(Dich_Chain1 *dst); >>> + >>> +static inline void >>> +_vtable_chain_merge(Dich_Chain1 *dst, const Dich_Chain1 *src) >>> +{ >>> + Eina_Bool writeable = EINA_FALSE; >>> + size_t j; >>> + const op_type_funcs *sf = src->chain2->funcs; >>> + op_type_funcs *df = dst->chain2->funcs; >>> + >>> + if (df == sf) >>> { >>> - chain1->funcs = calloc(DICH_CHAIN_LAST_SIZE, sizeof(* >>> (chain1->funcs))); >>> + /* Skip if the chain is the same. */ >>> + return; >>> + } >>> + >>> + for (j = 0 ; j < DICH_CHAIN_LAST_SIZE ; j++, df++, sf++) >>> + { >>> + if (sf->func && memcmp(df, sf, sizeof(*df))) >>> + { >>> + if (!writeable) >>> + { >>> + _vtable_chain_write_prepare(dst); >>> + df = dst->chain2->funcs + j; >>> + } >>> + >>> + memcpy(df, sf, sizeof(*df)); >>> + } >>> + } >>> +} >>> + >>> +static inline void >>> +_vtable_chain_write_prepare(Dich_Chain1 *dst) >>> +{ >>> + if (!dst->chain2) >>> + { >>> + _vtable_chain_alloc(dst); >>> + return; >>> + } >>> + else if (dst->chain2->refcount == 1) >>> + { >>> + /* We own it, no need to duplicate */ >>> + return; >>> + } >>> + >>> + Dich_Chain1 old; >>> + old.chain2 = dst->chain2; >>> + >>> + _vtable_chain_alloc(dst); >>> + _vtable_chain_merge(dst, &old); >>> + >>> + _vtable_chain2_unref(old.chain2); >>> +} >>> + >>> +static inline void >>> +_vtable_chain_copy_ref(Dich_Chain1 *dst, const Dich_Chain1 *src) >>> +{ >>> + if (dst->chain2) >>> + { >>> + _vtable_chain_merge(dst, src); >>> + } >>> + else >>> + { >>> + dst->chain2 = src->chain2; >>> + dst->chain2->refcount++; >>> } >>> } >>> >>> @@ -74,21 +143,9 @@ _vtable_copy_all(Eo_Vtable *dst, const Eo_Vtable *src) >>> Dich_Chain1 *dc1 = dst->chain; >>> for (i = 0 ; i < src->size ; i++, sc1++, dc1++) >>> { >>> - if (sc1->funcs) >>> + if (sc1->chain2) >>> { >>> - size_t j; >>> - >>> - _vtable_chain_alloc(dc1); >>> - >>> - const op_type_funcs *sf = sc1->funcs; >>> - op_type_funcs *df = dc1->funcs; >>> - for (j = 0 ; j < DICH_CHAIN_LAST_SIZE ; j++, df++, sf++) >>> - { >>> - if (sf->func) >>> - { >>> - memcpy(df, sf, sizeof(*df)); >>> - } >>> - } >>> + _vtable_chain_copy_ref(dc1, sc1); >>> } >>> } >>> } >>> @@ -100,9 +157,9 @@ _vtable_func_get(const Eo_Vtable *vtable, Efl_Object_Op >>> op) if (EINA_UNLIKELY(idx1 >= vtable->size)) >>> return NULL; >>> Dich_Chain1 *chain1 = &vtable->chain[idx1]; >>> - if (EINA_UNLIKELY(!chain1->funcs)) >>> + if (EINA_UNLIKELY(!chain1->chain2)) >>> return NULL; >>> - return &chain1->funcs[DICH_CHAIN_LAST(op)]; >>> + return &chain1->chain2->funcs[DICH_CHAIN_LAST(op)]; >>> } >>> >>> /* XXX: Only used for a debug message below. Doesn't matter that it's >>> slow. */ @@ -135,8 +192,8 @@ _vtable_func_set(Eo_Vtable *vtable, const >>> _Efl_Class *klass, Efl_Object_Op op, e op_type_funcs *fsrc; >>> size_t idx1 = DICH_CHAIN1(op); >>> Dich_Chain1 *chain1 = &vtable->chain[idx1]; >>> - _vtable_chain_alloc(chain1); >>> - fsrc = &chain1->funcs[DICH_CHAIN_LAST(op)]; >>> + _vtable_chain_write_prepare(chain1); >>> + fsrc = &chain1->chain2->funcs[DICH_CHAIN_LAST(op)]; >>> if (fsrc->src == klass) >>> { >>> const _Efl_Class *op_kls = _eo_op_class_get(op); >>> @@ -159,8 +216,8 @@ _vtable_func_clean_all(Eo_Vtable *vtable) >>> >>> for (i = 0 ; i < vtable->size ; i++, chain1++) >>> { >>> - if (chain1->funcs) >>> - free(chain1->funcs); >>> + if (chain1->chain2) >>> + _vtable_chain2_unref(chain1->chain2); >>> } >>> free(vtable->chain); >>> vtable->chain = NULL; >>> @@ -1849,4 +1906,3 @@ efl_manual_free(Eo *obj_id) >>> >>> return EINA_TRUE; >>> } >>> - >>> diff --git a/src/lib/eo/eo_private.h b/src/lib/eo/eo_private.h >>> index 37ee7c7..4096145 100644 >>> --- a/src/lib/eo/eo_private.h >>> +++ b/src/lib/eo/eo_private.h >>> @@ -123,6 +123,12 @@ struct _Eo_Object >>> Eina_Bool manual_free:1; >>> }; >>> >>> +/* How we search and store the implementations in classes. */ >>> +#define DICH_CHAIN_LAST_BITS 5 >>> +#define DICH_CHAIN_LAST_SIZE (1 << DICH_CHAIN_LAST_BITS) >>> +#define DICH_CHAIN1(x) ((x) >> DICH_CHAIN_LAST_BITS) >>> +#define DICH_CHAIN_LAST(x) ((x) & ((1 << DICH_CHAIN_LAST_BITS) - 1)) >>> + >>> /* FIXME: Change the type to something generic that makes sense for eo */ >>> typedef void (*eo_op_func_type)(Eo *, void *class_data, va_list *list); >>> >>> @@ -132,9 +138,15 @@ typedef struct >>> const _Efl_Class *src; >>> } op_type_funcs; >>> >>> +typedef struct _Dich_Chain2 >>> +{ >>> + op_type_funcs funcs[DICH_CHAIN_LAST_SIZE]; >>> + unsigned short refcount; >>> +} Dich_Chain2; >>> + >>> struct _Dich_Chain1 >>> { >>> - op_type_funcs *funcs; >>> + Dich_Chain2 *chain2; >>> }; >>> >>> typedef struct >>> >> >> >> ------------------------------------------------------------------------------ >> _______________________________________________ >> enlightenment-devel mailing list >> enlightenment-devel@lists.sourceforge.net >> https://lists.sourceforge.net/lists/listinfo/enlightenment-devel >> > > ------------------------------------------------------------------------------ _______________________________________________ enlightenment-devel mailing list enlightenment-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/enlightenment-devel