Eo: reduce memory usage across applications.

As described by Carsten in his email to edev ML titled:
"[E-devel] eo stability - i think we need to postpone that"
with the switch to Eo2 we significantly increased our usage of RW memory
pages, and thus significantly increased our memory usage when running
multiple applications.

The problem was that during the migration to Eo2 the op id cache and the
op description arrays were merged, causing the op description arrays to
no longer be RO. This patch enables users of Eo (mainly Eolian) to
declare those arrays as const (RO) again, saving that memory.

There might be performance implications with this patch. I had to remove
the op desc array sorting, and I used a hash table for the lookup. I
think the op desc sorting doesn't really affect performance because that
array is seldom accessed and is usually pretty short. The hash table
is not a problem either, because it's  behind the scenes, so it can be
changed to a more efficient data structure if the hash table is not good
enough. The hash table itself is also rarely accessed, so it's mostly
about memory.

Please keep an eye for any bugs, performance or excessive memory usage.
I believe this should be better on all fronts.

This commit *BREAKS ABI*.

@fix
This commit is contained in:
Tom Hacohen 2015-09-28 17:18:43 +01:00
parent 17f4c45fc9
commit e2344b9b9e
7 changed files with 83 additions and 140 deletions

View File

@ -19,7 +19,7 @@ static Eina_Hash *_funcs_params_init = NULL;
static const char
tmpl_eo_ops_desc[] = "\
static Eo_Op_Description _@#class_op_desc[] = {@#list_op\n};\n\n";
static const Eo_Op_Description _@#class_op_desc[] = {@#list_op\n};\n\n";
static const char
tmpl_events_desc[] = "\

View File

@ -132,6 +132,8 @@ enum _Eo_Op_Type
EO_OP_TYPE_INVALID = -1, /**< Invalid op. */
EO_OP_TYPE_REGULAR = 0, /**< Regular op. */
EO_OP_TYPE_CLASS, /**< Class op - a class op. Like static in Java/C++. */
EO_OP_TYPE_REGULAR_OVERRIDE, /**< Regular op override (previously defined) */
EO_OP_TYPE_CLASS_OVERRIDE, /**< Class op override (previously defined) */
};
/**
@ -344,7 +346,6 @@ typedef struct _Eo_Op_Description
{
void *api_func; /**< The EAPI function offering this op. (The name of the func on windows) */
void *func; /**< The static function to call for the op. */
Eo_Op op; /**< The op. */
Eo_Op_Type op_type; /**< The type of the Op. */
} Eo_Op_Description;
@ -360,7 +361,7 @@ struct _Eo_Class_Description
const char *name; /**< The name of the class. */
Eo_Class_Type type; /**< The type of the class. */
struct {
Eo_Op_Description *descs; /**< The op descriptions array of size count. */
const Eo_Op_Description *descs; /**< The op descriptions array of size count. */
size_t count; /**< Number of op descriptions. */
} ops; /**< The ops description, should be filled using #EO_CLASS_DESCRIPTION_OPS (later sorted by Eo). */
const Eo_Event_Description **events; /**< The event descriptions for this class. */
@ -469,7 +470,7 @@ EAPI extern Eo_Hook_Call eo_hook_call_post;
Eina_Bool ___is_main_loop = eina_main_loop_is(); \
static Eo_Op ___op = EO_NOOP; \
if (___op == EO_NOOP) \
___op = _eo_api_op_id_get(EO_FUNC_COMMON_OP_FUNC(Name), ___is_main_loop, __FILE__, __LINE__); \
___op = _eo_api_op_id_get(EO_FUNC_COMMON_OP_FUNC(Name)); \
if (!_eo_call_resolve(#Name, ___op, &___call, ___is_main_loop, __FILE__, __LINE__)) return DefRet; \
_Eo_##Name##_func _func_ = (_Eo_##Name##_func) ___call.func; \
@ -522,22 +523,19 @@ EAPI extern Eo_Hook_Call eo_hook_call_post;
EO_HOOK_CALL_PREPAREV(eo_hook_call_post, #Name, Arguments); \
}
// OP ID of an overriding function
#define EO_OP_OVERRIDE ((Eo_Op) -1)
#ifndef _WIN32
# define _EO_OP_API_ENTRY(a) a
#else
# define _EO_OP_API_ENTRY(a) #a
#endif
#define EO_OP_FUNC(_api, _private) { _EO_OP_API_ENTRY(_api), _private, EO_NOOP, EO_OP_TYPE_REGULAR }
#define EO_OP_CLASS_FUNC(_api, _private) { _EO_OP_API_ENTRY(_api), _private, EO_NOOP, EO_OP_TYPE_CLASS }
#define EO_OP_FUNC_OVERRIDE(_api, _private) { _EO_OP_API_ENTRY(_api), _private, EO_OP_OVERRIDE, EO_OP_TYPE_REGULAR }
#define EO_OP_CLASS_FUNC_OVERRIDE(_api, _private) { _EO_OP_API_ENTRY(_api), _private, EO_OP_OVERRIDE, EO_OP_TYPE_CLASS }
#define EO_OP_FUNC(_api, _private) { _EO_OP_API_ENTRY(_api), _private, EO_OP_TYPE_REGULAR }
#define EO_OP_CLASS_FUNC(_api, _private) { _EO_OP_API_ENTRY(_api), _private, EO_OP_TYPE_CLASS }
#define EO_OP_FUNC_OVERRIDE(_api, _private) { _EO_OP_API_ENTRY(_api), _private, EO_OP_TYPE_REGULAR_OVERRIDE }
#define EO_OP_CLASS_FUNC_OVERRIDE(_api, _private) { _EO_OP_API_ENTRY(_api), _private, EO_OP_TYPE_CLASS_OVERRIDE }
// returns the OP id corresponding to the given api_func
EAPI Eo_Op _eo_api_op_id_get(const void *api_func, Eina_Bool is_main_loop, const char *file, int line);
EAPI Eo_Op _eo_api_op_id_get(const void *api_func);
// gets the real function pointer and the object data
EAPI Eina_Bool _eo_call_resolve(const char *func_name, const Eo_Op op, Eo_Op_Call_Data *call, Eina_Bool is_main_loop, const char *file, int line);

View File

@ -25,6 +25,8 @@ static _Eo_Class **_eo_classes;
static Eo_Id _eo_classes_last_id;
static int _eo_init_count = 0;
static Eo_Op _eo_ops_last_id = 0;
static Eina_Hash *_ops_storage = NULL;
static Eina_Spinlock _ops_storage_lock;
static size_t _eo_sz = 0;
static size_t _eo_class_sz = 0;
@ -34,7 +36,6 @@ static inline void *_eo_data_scope_get(const _Eo_Object *obj, const _Eo_Class *k
static inline void *_eo_data_xref_internal(const char *file, int line, _Eo_Object *obj, const _Eo_Class *klass, const _Eo_Object *ref_obj);
static inline void _eo_data_xunref_internal(_Eo_Object *obj, void *data, const _Eo_Object *ref_obj);
static const _Eo_Class *_eo_op_class_get(Eo_Op op);
static const char * _eo_op_id_name_get(Eo_Op op);
/* Start of Dich */
@ -113,9 +114,8 @@ _dich_func_set(_Eo_Class *klass, Eo_Op op, eo_op_func_type func)
if (fsrc->src == klass)
{
const _Eo_Class *op_kls = _eo_op_class_get(op);
const char *op_name = _eo_op_id_name_get(op);
ERR("Class '%s': Overriding func %p for op %d (%s:'%s') with %p.",
klass->desc->name, fsrc->func, op, op_kls->desc->name, op_name, func);
ERR("Class '%s': Overriding func %p for op %d (%s) with %p.",
klass->desc->name, fsrc->func, op, op_kls->desc->name, func);
return EINA_FALSE;
}
@ -191,33 +191,6 @@ _eo_op_class_get(Eo_Op op)
return NULL;
}
static const Eo_Op_Description *
_eo_op_id_desc_get(Eo_Op op)
{
unsigned int i;
const _Eo_Class *klass;
const Eo_Op_Description *op_descs;
if (op == EO_NOOP)
return NULL;
klass = _eo_op_class_get(op);
if (klass)
{
DBG("klass %p %s", klass, klass->desc->name);
op_descs = klass->desc->ops.descs;
for (i = 0; i < klass->desc->ops.count; i++)
{
if (op_descs[i].op == op)
return &op_descs[i];
}
}
return NULL;
}
static const char *
_eo_op_desc_name_get(const Eo_Op_Description *desc)
{
@ -240,13 +213,6 @@ _eo_op_desc_name_get(const Eo_Op_Description *desc)
return fct_name;
}
static const char *
_eo_op_id_name_get(Eo_Op op)
{
const Eo_Op_Description *desc = _eo_op_id_desc_get(op);
return _eo_op_desc_name_get(desc);
}
static inline const op_type_funcs *
_eo_kls_itr_next(const _Eo_Class *orig_kls, const _Eo_Class *cur_klass, Eo_Op op)
{
@ -582,15 +548,13 @@ _eo_do_end(void)
}
EAPI Eina_Bool
_eo_call_resolve(const char *func_name, const Eo_Op op, Eo_Op_Call_Data *call, Eina_Bool is_main_loop, const char *file, int line)
_eo_call_resolve(const char *func_name, const Eo_Op op, Eo_Op_Call_Data *call, Eina_Bool is_main_loop, const char *file, int line)
{
Eo_Stack_Frame *fptr;
const _Eo_Class *klass;
const op_type_funcs *func;
Eina_Bool is_obj;
if (op == EO_NOOP) return EINA_FALSE;
fptr = _eo_call_stack_get(is_main_loop)->frame_ptr;
if (EINA_UNLIKELY(!fptr->o.obj))
@ -600,6 +564,15 @@ EAPI Eina_Bool
klass = (is_obj) ? fptr->o.obj->klass : fptr->o.kls;
if (op == EO_NOOP)
{
ERR("%s:%d: unable to resolve %s api func '%s' in class '%s'.",
file, line, (!is_obj ? "class" : "regular"),
func_name, klass->desc->name);
return EINA_FALSE;
}
/* If we have a current class, we need to itr to the next. */
if (fptr->cur_klass)
{
@ -718,25 +691,17 @@ _eo_api_desc_get(const void *api_func, const _Eo_Class *klass, const _Eo_Class *
{
for (kls_itr = klass->mro ; *kls_itr ; kls_itr++)
{
unsigned int i;
cur_klass = *kls_itr;
op_descs = cur_klass->desc->ops.descs;
#ifndef _WIN32
int imin, imax, imid;
imin = 0;
imax = cur_klass->desc->ops.count - 1;
while (imax >= imin)
for (i = 0, op_desc = op_descs; i < cur_klass->desc->ops.count; i++, op_desc++)
{
imid = (imax + imin) / 2;
op_desc = op_descs + imid;
if (op_desc->api_func > api_func)
imin = imid + 1;
else if (op_desc->api_func < api_func)
imax = imid - 1;
else
return op_desc;
if (op_desc->api_func == api_func)
{
return op_desc;
}
}
#else
/* On Windows, DLL API's will be exported using the dllexport flag.
@ -746,7 +711,7 @@ _eo_api_desc_get(const void *api_func, const _Eo_Class *klass, const _Eo_Class *
* them. We fallback to plain string comparison based on the
* function name itself. Slow, but this should rarely happen.
*/
for (unsigned int i = 0; i < cur_klass->desc->ops.count; i++)
for (i = 0; i < cur_klass->desc->ops.count; i++)
{
if (((op_descs[i].api_func != NULL) && (op_descs[i].api_func != ((void (*)())-1))) &&
(api_func && !strcmp(api_func, op_descs[i].api_func)))
@ -772,45 +737,13 @@ _eo_api_desc_get(const void *api_func, const _Eo_Class *klass, const _Eo_Class *
}
EAPI Eo_Op
_eo_api_op_id_get(const void *api_func, Eina_Bool is_main_loop, const char *file, int line)
_eo_api_op_id_get(const void *api_func)
{
const Eo_Op_Description *desc;
const _Eo_Class *klass;
Eo_Call_Stack *stack = _eo_call_stack_get(is_main_loop);
eina_spinlock_take(&_ops_storage_lock);
Eo_Op op = (uintptr_t) eina_hash_find(_ops_storage, &api_func);
eina_spinlock_release(&_ops_storage_lock);
Eina_Bool class_ref = _eo_is_a_class(stack->frame_ptr->eo_id);
if (EINA_UNLIKELY(!stack->frame_ptr->o.obj))
return EO_NOOP;
if (class_ref)
klass = stack->frame_ptr->o.kls;
else
klass = stack->frame_ptr->o.obj->klass;
desc = _eo_api_desc_get(api_func, klass, klass->extensions);
if (desc == NULL)
{
const char *fct_name = _eo_op_desc_name_get(desc);
ERR("in %s:%d: unable to resolve %s api func '%s' %p in class '%s'.",
file, line, (class_ref ? "class" : "regular"),
fct_name, api_func, klass->desc->name);
return EO_NOOP;
}
return desc->op;
}
static int
eo_api_funcs_cmp(const void *p1, const void *p2)
{
const Eo_Op_Description *op1, *op2;
op1 = (Eo_Op_Description *) p1;
op2 = (Eo_Op_Description *) p2;
if (op1->api_func > op2->api_func) return -1;
else if (op1->api_func < op2->api_func) return 1;
else return 0;
return op;
}
static Eina_Bool
@ -819,9 +752,8 @@ _eo_class_funcs_set(_Eo_Class *klass)
unsigned int i;
int op_id;
const void *last_api_func;
const Eo_Op_Description *api_desc;
Eo_Op_Description *op_desc;
Eo_Op_Description *op_descs;
const Eo_Op_Description *op_desc;
const Eo_Op_Description *op_descs;
op_id = klass->base_id;
op_descs = klass->desc->ops.descs;
@ -830,31 +762,37 @@ _eo_class_funcs_set(_Eo_Class *klass)
if (!op_descs) return EINA_TRUE;
qsort((void*)op_descs, klass->desc->ops.count, sizeof(Eo_Op_Description), eo_api_funcs_cmp);
last_api_func = NULL;
for (i = 0, op_desc = op_descs; i < klass->desc->ops.count; i++, op_desc++)
{
if(op_desc->api_func == NULL)
Eo_Op op = EO_NOOP;
if (op_desc->api_func == NULL)
{
ERR("Class '%s': NULL API not allowed (%d NULL->%p '%s').",
klass->desc->name, op_desc->op, op_desc->func, _eo_op_desc_name_get(op_desc));
ERR("Class '%s': NULL API not allowed (NULL->%p '%s').",
klass->desc->name, op_desc->func, _eo_op_desc_name_get(op_desc));
return EINA_FALSE;
}
if (op_desc->op == EO_NOOP)
if ((op_desc->op_type == EO_OP_TYPE_REGULAR) || (op_desc->op_type == EO_OP_TYPE_CLASS))
{
if (op_desc->api_func == last_api_func)
{
ERR("Class '%s': API previously defined (%d %p->%p '%s').",
klass->desc->name, op_desc->op, op_desc->api_func, op_desc->func, _eo_op_desc_name_get(op_desc));
ERR("Class '%s': API previously defined (%p->%p '%s').",
klass->desc->name, op_desc->api_func, op_desc->func, _eo_op_desc_name_get(op_desc));
return EINA_FALSE;
}
op_desc->op = op_id;
op = op_id;
eina_spinlock_take(&_ops_storage_lock);
eina_hash_add(_ops_storage, &op_desc->api_func, (void *) (uintptr_t) op);
eina_spinlock_release(&_ops_storage_lock);
op_id++;
}
else if (op_desc->op == EO_OP_OVERRIDE)
else if ((op_desc->op_type == EO_OP_TYPE_REGULAR_OVERRIDE) || (op_desc->op_type == EO_OP_TYPE_CLASS_OVERRIDE))
{
const Eo_Op_Description *api_desc;
api_desc = _eo_api_desc_get(op_desc->api_func, klass->parent, klass->extensions);
if (api_desc == NULL)
@ -864,12 +802,19 @@ _eo_class_funcs_set(_Eo_Class *klass)
return EINA_FALSE;
}
op_desc->op = api_desc->op;
op = _eo_api_op_id_get(op_desc->api_func);
}
DBG(" %4d %p->%p '%s'", op_desc->op, op_desc->api_func, op_desc->func, _eo_op_desc_name_get(op_desc));
if (op == EO_NOOP)
{
ERR("Class '%s': Invalid op 'EO_NOOP' (%p->%p '%s').",
klass->desc->name, op_desc->api_func, op_desc->func, _eo_op_desc_name_get(op_desc));
return EINA_FALSE;
}
if (!_dich_func_set(klass, op_desc->op, op_desc->func))
DBG("%p->%p '%s'", op_desc->api_func, op_desc->func, _eo_op_desc_name_get(op_desc));
if (!_dich_func_set(klass, op, op_desc->func))
return EINA_FALSE;
last_api_func = op_desc->api_func;
@ -1019,17 +964,6 @@ _eo_add_end(void)
/*****************************************************************************/
#define _EO_OP_ERR_NO_OP_PRINT(file, line, op, klass) \
do \
{ \
const _Eo_Class *op_klass = _eo_op_class_get(op); \
const char *_dom_name = (op_klass) ? op_klass->desc->name : NULL; \
ERR("in %s:%d: Can't execute function %s:%s (op 0x%x) for class '%s'. Aborting.", \
file, line, _dom_name, _eo_op_id_name_get(op), op, \
(klass) ? klass->desc->name : NULL); \
} \
while (0)
EAPI const Eo_Class *
eo_class_get(const Eo *eo_id)
{
@ -1834,12 +1768,20 @@ eo_init(void)
return EINA_FALSE;
}
if (!eina_spinlock_new(&_ops_storage_lock))
{
EINA_LOG_ERR("Could not init lock.");
return EINA_FALSE;
}
eina_magic_string_static_set(EO_EINA_MAGIC, EO_EINA_MAGIC_STR);
eina_magic_string_static_set(EO_FREED_EINA_MAGIC,
EO_FREED_EINA_MAGIC_STR);
eina_magic_string_static_set(EO_CLASS_EINA_MAGIC,
EO_CLASS_EINA_MAGIC_STR);
_ops_storage = eina_hash_pointer_new(NULL);
#ifdef EO_DEBUG
/* Call it just for coverage purposes. Ugly I know, but I like it better than
* casting everywhere else. */
@ -1890,6 +1832,9 @@ eo_shutdown(void)
if (_eo_classes)
free(_eo_classes);
eina_hash_free(_ops_storage);
eina_spinlock_free(&_ops_storage_lock);
eina_spinlock_free(&_eo_class_creation_lock);
if (_eo_call_stack_key != 0)

View File

@ -37,7 +37,7 @@ START_TEST(eo_api_not_implemented_call)
Eo *obj = eo_add(SIMPLE_CLASS, NULL);
fail_if(!obj);
TEST_EO_ERROR("_eo_api_op_id_get", "in %s:%d: unable to resolve %s api func '%s' %p in class '%s'.");
TEST_EO_ERROR("_eo_call_resolve", "%s:%d: unable to resolve %s api func '%s' in class '%s'.");
eo_do(obj, simple_no_implementation());
fail_unless(ctx.did);

View File

@ -230,7 +230,7 @@ START_TEST(eo_null_api)
NULL
};
TEST_EO_ERROR("_eo_class_funcs_set", "Class '%s': NULL API not allowed (%d NULL->%p '%s').");
TEST_EO_ERROR("_eo_class_funcs_set", "Class '%s': NULL API not allowed (NULL->%p '%s').");
klass = eo_class_new(&class_desc, NULL, NULL);
fail_if(klass);
fail_unless(ctx.did);
@ -295,7 +295,7 @@ START_TEST(eo_api_redefined)
NULL
};
TEST_EO_ERROR("_eo_class_funcs_set", "Class '%s': API previously defined (%d %p->%p '%s').");
TEST_EO_ERROR("_eo_class_funcs_set", "Class '%s': API previously defined (%p->%p '%s').");
klass = eo_class_new(&class_desc, NULL, NULL);
fail_if(klass);
fail_unless(ctx.did);
@ -328,7 +328,7 @@ START_TEST(eo_dich_func_override)
NULL
};
TEST_EO_ERROR("_dich_func_set", "Class '%s': Overriding func %p for op %d (%s:'%s') with %p.");
TEST_EO_ERROR("_dich_func_set", "Class '%s': Overriding func %p for op %d (%s) with %p.");
klass = eo_class_new(&class_desc, SIMPLE_CLASS, NULL);
fail_if(klass);
fail_unless(ctx.did);

View File

@ -25,7 +25,7 @@ int _class_simple_bar(Eo *obj, Evas_Simple_Data *pd, int x);
EOAPI EO_FUNC_BODYV(evas_obj_simple_bar, int, 0, EO_FUNC_CALL(x), int x);
static Eo_Op_Description _class_simple_op_desc[] = {
static const Eo_Op_Description _class_simple_op_desc[] = {
EO_OP_FUNC(evas_obj_simple_a_set, _class_simple_a_set),
EO_OP_FUNC(evas_obj_simple_a_get, _class_simple_a_get),
EO_OP_FUNC(evas_obj_simple_foo, __eolian_class_simple_foo),
@ -42,4 +42,4 @@ static const Eo_Class_Description _class_simple_class_desc = {
NULL
};
EO_DEFINE_CLASS(class_simple_class_get, &_class_simple_class_desc, NULL, NULL);
EO_DEFINE_CLASS(class_simple_class_get, &_class_simple_class_desc, NULL, NULL);

View File

@ -53,7 +53,7 @@ static void __eolian_override_base_z_set(Eo *obj EINA_UNUSED, Override_Data *pd
}
static Eo_Op_Description _override_op_desc[] = {
static const Eo_Op_Description _override_op_desc[] = {
EO_OP_FUNC_OVERRIDE(base_constructor, _override_base_constructor),
EO_OP_FUNC_OVERRIDE(base_z_get, __eolian_override_base_z_get),
EO_OP_FUNC_OVERRIDE(base_z_set, __eolian_override_base_z_set),
@ -78,4 +78,4 @@ static const Eo_Class_Description _override_class_desc = {
NULL
};
EO_DEFINE_CLASS(override_class_get, &_override_class_desc, BASE_CLASS, NULL);
EO_DEFINE_CLASS(override_class_get, &_override_class_desc, BASE_CLASS, NULL);