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() ... :)

> --
> 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
> 


-- 
------------- Codito, ergo sum - "I code, therefore I am" --------------
The Rasterman (Carsten Haitzler)    ras...@rasterman.com


------------------------------------------------------------------------------
_______________________________________________
enlightenment-devel mailing list
enlightenment-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/enlightenment-devel

Reply via email to