summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean Guyomarc'h <jean@guyomarch.bzh>2018-01-16 14:58:38 +0900
committerCarsten Haitzler (Rasterman) <raster@rasterman.com>2018-01-16 17:50:46 +0900
commit34d9f2070696027199a56cb621c0526ea1430e8f (patch)
treef5d4bd8dea83a82fcb4bfb52225bb7487f061859
parent574ef9a5e53ac2ecabc14fd02be7e55cd90a9b25 (diff)
eina: remove usless newline
Summary: ecore_evas: remove debug eina: unregister log level when done with Fixes a constant memory leak. eina: introduce EINA_HOT and EINA_COLD These attributes respectivelly expand to __attribute__ ((hot)) and __attribute__ ((cold)) when available. They allow to mark functions are being hot/cold (frequently used or not) as well as to qualify labels within a function (likely/unlikely branches). eo: speed-up generated calls by removing call cache The call cache needed to by thread-local, to avoid concurrency issues. Problem with TLS is that is adds an extra overhead, which appears to be greater than the optimization the cache provides. Op is naturally atomic, because it is an unsigned integer. As such, it cannot be tempered with while another thread is reading it. When entering the generated function, the first operation done is reading 'op'. If we have concurrency, we will have access sequences returning either EFL_NOOP or a VALID op, because 'op' is not set until the very end of the function, when everything has been computed. As such, we now use the 'op' atomic integer to instore a lock-free/wait-free mechanism, which allows to drop the TLS nature of the cache, speeding up the access to the cache, and therefore making functions execute faster. We don't test anymore the generation count. This can be put as a limitation. If means that if you call efl_object_shutdown() and re-initialize it later with different data, opcodes will be invalid. I am not sure there is any usecase for this to ever happen. We could move all the caches in a dedicated section, that can be overwritten after a call to efl_object_shutdown(), but I am not sure it will be very portable. Benchmark: mean over 3 executions of ELM_TEST_AUTOBOUNCE=100 time elementary_test -to genlist ``` BEFORE AFTER ------------------------------------------------------------ time (ns) 11114111647.0 9147676220.0 frames 2872.3333333333335 2904.6666666666665 time per frame (ns) 3869364.6666666665 3149535.3333333335 user time (s) 11.096666666666666 9.22 cpu (%) 22.666666666666668 18.333333333333332 ``` Ref T6580 Reviewers: raster, cedric Subscribers: cedric, jpeg Maniphest Tasks: T6580 Differential Revision: https://phab.enlightenment.org/D5738
-rw-r--r--src/lib/ecore_evas/ecore_evas_private.h2
-rw-r--r--src/lib/eina/eina_cow.c1
-rw-r--r--src/lib/eina/eina_safepointer.c2
-rw-r--r--src/lib/eina/eina_types.h8
-rw-r--r--src/lib/eo/Eo.h56
-rw-r--r--src/lib/eo/eo.c64
6 files changed, 33 insertions, 100 deletions
diff --git a/src/lib/ecore_evas/ecore_evas_private.h b/src/lib/ecore_evas/ecore_evas_private.h
index 7b83589347..13002402a4 100644
--- a/src/lib/ecore_evas/ecore_evas_private.h
+++ b/src/lib/ecore_evas/ecore_evas_private.h
@@ -347,7 +347,7 @@ struct _Ecore_Evas
347 } delayed; 347 } delayed;
348 348
349 int refcount; 349 int refcount;
350#define ECORE_EVAS_ASYNC_RENDER_DEBUG 1 /* TODO: remove me */ 350//#define ECORE_EVAS_ASYNC_RENDER_DEBUG 1 /* TODO: remove me */
351#ifdef ECORE_EVAS_ASYNC_RENDER_DEBUG 351#ifdef ECORE_EVAS_ASYNC_RENDER_DEBUG
352 double async_render_start; 352 double async_render_start;
353#endif 353#endif
diff --git a/src/lib/eina/eina_cow.c b/src/lib/eina/eina_cow.c
index 73a65e5f30..8ec86cfd79 100644
--- a/src/lib/eina/eina_cow.c
+++ b/src/lib/eina/eina_cow.c
@@ -320,6 +320,7 @@ eina_cow_init(void)
320Eina_Bool 320Eina_Bool
321eina_cow_shutdown(void) 321eina_cow_shutdown(void)
322{ 322{
323 eina_log_domain_unregister(_eina_cow_log_dom);
323 eina_mempool_del(gc_pool); 324 eina_mempool_del(gc_pool);
324 return EINA_TRUE; 325 return EINA_TRUE;
325} 326}
diff --git a/src/lib/eina/eina_safepointer.c b/src/lib/eina/eina_safepointer.c
index 1a19f851c7..8f9d4b62a9 100644
--- a/src/lib/eina/eina_safepointer.c
+++ b/src/lib/eina/eina_safepointer.c
@@ -370,7 +370,7 @@ eina_safepointer_init(void)
370 370
371 DBG("entry[Size, Align] = { %zu, %u }", 371 DBG("entry[Size, Align] = { %zu, %u }",
372 sizeof (Eina_Memory_Entry), eina_mempool_alignof(sizeof (Eina_Memory_Entry))); 372 sizeof (Eina_Memory_Entry), eina_mempool_alignof(sizeof (Eina_Memory_Entry)));
373 DBG("table[Size, Align] = { %zu, %u }\n", 373 DBG("table[Size, Align] = { %zu, %u }",
374 sizeof (Eina_Memory_Table), eina_mempool_alignof(sizeof (Eina_Memory_Table))); 374 sizeof (Eina_Memory_Table), eina_mempool_alignof(sizeof (Eina_Memory_Table)));
375 375
376 return EINA_TRUE; 376 return EINA_TRUE;
diff --git a/src/lib/eina/eina_types.h b/src/lib/eina/eina_types.h
index 94e6ebc6f3..3d15a3f42d 100644
--- a/src/lib/eina/eina_types.h
+++ b/src/lib/eina/eina_types.h
@@ -184,6 +184,14 @@
184# define EINA_PURE 184# define EINA_PURE
185# endif 185# endif
186 186
187# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
188# define EINA_HOT __attribute__ ((hot))
189# define EINA_COLD __attribute__ ((cold))
190# else
191# define EINA_HOT
192# define EINA_COLD
193# endif
194
187# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) 195# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
188# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 3) 196# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 3)
189# define EINA_PRINTF(fmt, arg) __attribute__((__format__ (__gnu_printf__, fmt, arg))) 197# define EINA_PRINTF(fmt, arg) __attribute__((__format__ (__gnu_printf__, fmt, arg)))
diff --git a/src/lib/eo/Eo.h b/src/lib/eo/Eo.h
index 3ea20ec2ff..8b489ab72a 100644
--- a/src/lib/eo/Eo.h
+++ b/src/lib/eo/Eo.h
@@ -1159,37 +1159,6 @@ typedef struct _Efl_Object_Op_Call_Data
1159 void *extn4; // for future use to avoid ABI issues 1159 void *extn4; // for future use to avoid ABI issues
1160} Efl_Object_Op_Call_Data; 1160} Efl_Object_Op_Call_Data;
1161 1161
1162#define EFL_OBJECT_CALL_CACHE_SIZE 1
1163
1164typedef struct _Efl_Object_Call_Cache_Index
1165{
1166 const void *klass;
1167} Efl_Object_Call_Cache_Index;
1168
1169typedef struct _Efl_Object_Call_Cache_Entry
1170{
1171 const void *func;
1172} Efl_Object_Call_Cache_Entry;
1173
1174typedef struct _Efl_Object_Call_Cache_Off
1175{
1176 int off;
1177} Efl_Object_Call_Cache_Off;
1178
1179typedef struct _Efl_Object_Call_Cache
1180{
1181#if EFL_OBJECT_CALL_CACHE_SIZE > 0
1182 Efl_Object_Call_Cache_Index index[EFL_OBJECT_CALL_CACHE_SIZE];
1183 Efl_Object_Call_Cache_Entry entry[EFL_OBJECT_CALL_CACHE_SIZE];
1184 Efl_Object_Call_Cache_Off off [EFL_OBJECT_CALL_CACHE_SIZE];
1185# if EFL_OBJECT_CALL_CACHE_SIZE > 1
1186 int next_slot;
1187# endif
1188#endif
1189 Efl_Object_Op op;
1190 unsigned int generation;
1191} Efl_Object_Call_Cache;
1192
1193// to pass the internal function call to EFL_FUNC_BODY (as Func parameter) 1162// to pass the internal function call to EFL_FUNC_BODY (as Func parameter)
1194#define EFL_FUNC_CALL(...) __VA_ARGS__ 1163#define EFL_FUNC_CALL(...) __VA_ARGS__
1195 1164
@@ -1205,17 +1174,18 @@ typedef struct _Efl_Object_Call_Cache
1205# define EFL_FUNC_TLS __thread 1174# define EFL_FUNC_TLS __thread
1206#endif 1175#endif
1207 1176
1177
1208// cache OP id, get real fct and object data then do the call 1178// cache OP id, get real fct and object data then do the call
1209#define EFL_FUNC_COMMON_OP(Obj, Name, DefRet) \ 1179#define EFL_FUNC_COMMON_OP(Obj, Name, DefRet) \
1210 static EFL_FUNC_TLS Efl_Object_Call_Cache ___cache; /* static 0 by default */ \ 1180 static Efl_Object_Op ___op; /* static 0 by default */ \
1211 Efl_Object_Op_Call_Data ___call; \ 1181 Efl_Object_Op_Call_Data ___call; \
1212 _Eo_##Name##_func _func_; \ 1182 _Eo_##Name##_func _func_; \
1213 if (EINA_UNLIKELY((___cache.op == EFL_NOOP) || \ 1183 if (EINA_UNLIKELY(___op == EFL_NOOP)) \
1214 (___cache.generation != _efl_object_init_generation))) \
1215 goto __##Name##_op_create; /* yes a goto - see below */ \ 1184 goto __##Name##_op_create; /* yes a goto - see below */ \
1216 __##Name##_op_create_done: \ 1185 __##Name##_op_create_done: EINA_HOT; \
1217 if (!_efl_object_call_resolve((Eo *) Obj, #Name, &___call, &___cache, \ 1186 if (EINA_UNLIKELY(!_efl_object_call_resolve( \
1218 __FILE__, __LINE__)) goto __##Name##_failed; \ 1187 (Eo *) Obj, #Name, &___call, ___op, __FILE__, __LINE__))) \
1188 goto __##Name##_failed; \
1219 _func_ = (_Eo_##Name##_func) ___call.func; 1189 _func_ = (_Eo_##Name##_func) ___call.func;
1220 1190
1221// This looks ugly with gotos BUT it moves rare "init" handling code 1191// This looks ugly with gotos BUT it moves rare "init" handling code
@@ -1227,13 +1197,11 @@ typedef struct _Efl_Object_Call_Cache
1227// of the cacheline that was already fetched should yield better cache 1197// of the cacheline that was already fetched should yield better cache
1228// hits. 1198// hits.
1229#define EFL_FUNC_COMMON_OP_END(Obj, Name, DefRet, ErrorCase) \ 1199#define EFL_FUNC_COMMON_OP_END(Obj, Name, DefRet, ErrorCase) \
1230__##Name##_op_create: \ 1200__##Name##_op_create: EINA_COLD; \
1231 if (EINA_UNLIKELY(___cache.op != EFL_NOOP)) memset(&___cache, 0, sizeof(___cache)); \ 1201 ___op = _efl_object_op_api_id_get(EFL_FUNC_COMMON_OP_FUNC(Name), Obj, #Name, __FILE__, __LINE__); \
1232 ___cache.op = _efl_object_op_api_id_get(EFL_FUNC_COMMON_OP_FUNC(Name), Obj, #Name, __FILE__, __LINE__); \ 1202 if (EINA_UNLIKELY(___op == EFL_NOOP)) goto __##Name##_failed; \
1233 if (___cache.op == EFL_NOOP) goto __##Name##_failed; \
1234 ___cache.generation = _efl_object_init_generation; \
1235 goto __##Name##_op_create_done; \ 1203 goto __##Name##_op_create_done; \
1236__##Name##_failed: \ 1204__##Name##_failed: EINA_COLD; \
1237 ErrorCase \ 1205 ErrorCase \
1238 return DefRet; 1206 return DefRet;
1239#define _EFL_OBJECT_API_BEFORE_HOOK 1207#define _EFL_OBJECT_API_BEFORE_HOOK
@@ -1335,7 +1303,7 @@ EAPI Efl_Object_Op _efl_object_api_op_id_get(const void *api_func) EINA_DEPRECAT
1335EAPI Efl_Object_Op _efl_object_op_api_id_get(const void *api_func, const Eo *obj, const char *api_func_name, const char *file, int line) EINA_ARG_NONNULL(1, 2, 3, 4) EINA_WARN_UNUSED_RESULT; 1303EAPI Efl_Object_Op _efl_object_op_api_id_get(const void *api_func, const Eo *obj, const char *api_func_name, const char *file, int line) EINA_ARG_NONNULL(1, 2, 3, 4) EINA_WARN_UNUSED_RESULT;
1336 1304
1337// gets the real function pointer and the object data 1305// gets the real function pointer and the object data
1338EAPI Eina_Bool _efl_object_call_resolve(Eo *obj, const char *func_name, Efl_Object_Op_Call_Data *call, Efl_Object_Call_Cache *callcache, const char *file, int line); 1306EAPI Eina_Bool _efl_object_call_resolve(Eo *obj, const char *func_name, Efl_Object_Op_Call_Data *call, Efl_Object_Op op, const char *file, int line);
1339 1307
1340// end of the eo call barrier, unref the obj 1308// end of the eo call barrier, unref the obj
1341EAPI void _efl_object_call_end(Efl_Object_Op_Call_Data *call); 1309EAPI void _efl_object_call_end(Efl_Object_Op_Call_Data *call);
diff --git a/src/lib/eo/eo.c b/src/lib/eo/eo.c
index 50df250acf..0db34fc35c 100644
--- a/src/lib/eo/eo.c
+++ b/src/lib/eo/eo.c
@@ -439,15 +439,14 @@ efl_cast(const Eo *eo_id, const Efl_Class *cur_klass)
439} 439}
440 440
441EAPI Eina_Bool 441EAPI Eina_Bool
442_efl_object_call_resolve(Eo *eo_id, const char *func_name, Efl_Object_Op_Call_Data *call, Efl_Object_Call_Cache *cache, const char *file, int line) 442_efl_object_call_resolve(Eo *eo_id, const char *func_name, Efl_Object_Op_Call_Data *call, Efl_Object_Op op, const char *file, int line)
443{ 443{
444 const _Efl_Class *klass, *inputklass, *main_klass; 444 const _Efl_Class *klass, *main_klass;
445 const _Efl_Class *cur_klass = NULL; 445 const _Efl_Class *cur_klass = NULL;
446 _Eo_Object *obj = NULL; 446 _Eo_Object *obj = NULL;
447 const Eo_Vtable *vtable = NULL; 447 const Eo_Vtable *vtable = NULL;
448 const op_type_funcs *func; 448 const op_type_funcs *func;
449 Eina_Bool is_obj; 449 Eina_Bool is_obj;
450 Eina_Bool is_override = EINA_FALSE;
451 Eina_Bool super = EINA_TRUE; 450 Eina_Bool super = EINA_TRUE;
452 451
453 if (EINA_UNLIKELY(!eo_id)) return EINA_FALSE; 452 if (EINA_UNLIKELY(!eo_id)) return EINA_FALSE;
@@ -456,14 +455,13 @@ _efl_object_call_resolve(Eo *eo_id, const char *func_name, Efl_Object_Op_Call_Da
456 455
457 is_obj = _eo_is_a_obj(eo_id); 456 is_obj = _eo_is_a_obj(eo_id);
458 457
459 if (is_obj) 458 if (EINA_LIKELY(is_obj == EINA_TRUE))
460 { 459 {
461 EO_OBJ_POINTER_RETURN_VAL_PROXY(eo_id, _obj, EINA_FALSE); 460 EO_OBJ_POINTER_RETURN_VAL_PROXY(eo_id, _obj, EINA_FALSE);
462 461
463 obj = _obj; 462 obj = _obj;
464 klass = _obj->klass; 463 klass = _obj->klass;
465 vtable = EO_VTABLE(obj); 464 vtable = EO_VTABLE(obj);
466 is_override = _obj_is_override(obj);
467 if (EINA_UNLIKELY(_obj->cur_klass != NULL)) 465 if (EINA_UNLIKELY(_obj->cur_klass != NULL))
468 { 466 {
469 // YES this is a goto with a label to return. this is a 467 // YES this is a goto with a label to return. this is a
@@ -485,9 +483,7 @@ obj_super_back:
485 } 483 }
486ok_klass_back: 484ok_klass_back:
487 485
488 inputklass = main_klass = klass; 486 main_klass = klass;
489
490 if (!cache->op) goto err_cache_op;
491 487
492 /* If we have a current class, we need to itr to the next. */ 488 /* If we have a current class, we need to itr to the next. */
493 if (cur_klass) 489 if (cur_klass)
@@ -499,30 +495,7 @@ ok_klass_back:
499 } 495 }
500 else 496 else
501 { 497 {
502# if EFL_OBJECT_CALL_CACHE_SIZE > 0 498 func = _vtable_func_get(vtable, op);
503 if (!is_override)
504 {
505# if EFL_OBJECT_CALL_CACHE_SIZE > 1
506 int i;
507
508 for (i = 0; i < EFL_OBJECT_CALL_CACHE_SIZE; i++)
509# else
510 const int i = 0;
511# endif
512 {
513 if ((const void *)inputklass == cache->index[i].klass)
514 {
515 func = (const op_type_funcs *)cache->entry[i].func;
516 call->func = func->func;
517 if (is_obj)
518 call->data = (char *)obj + cache->off[i].off;
519 if (EINA_UNLIKELY(!call->func)) goto err_cache_op;
520 return EINA_TRUE;
521 }
522 }
523 }
524#endif
525 func = _vtable_func_get(vtable, cache->op);
526 // this is not very likely to happen - but may if its an invalid 499 // this is not very likely to happen - but may if its an invalid
527 // call or a composite object, but either way, it's not very likely 500 // call or a composite object, but either way, it's not very likely
528 // so make it a goto to save on instruction cache 501 // so make it a goto to save on instruction cache
@@ -536,22 +509,6 @@ ok_cur_klass_back:
536 509
537 if (is_obj) call->data = _efl_data_scope_get(obj, func->src); 510 if (is_obj) call->data = _efl_data_scope_get(obj, func->src);
538 511
539# if EFL_OBJECT_CALL_CACHE_SIZE > 0
540 if (!cur_klass && !is_override)
541 {
542# if EFL_OBJECT_CALL_CACHE_SIZE > 1
543 const int slot = cache->next_slot;
544# else
545 const int slot = 0;
546# endif
547 cache->index[slot].klass = (const void *)inputklass;
548 cache->entry[slot].func = (const void *)func;
549 cache->off[slot].off = (int)((long)((char *)call->data - (char *)obj));
550# if EFL_OBJECT_CALL_CACHE_SIZE > 1
551 cache->next_slot = (slot + 1) % EFL_OBJECT_CALL_CACHE_SIZE;
552# endif
553 }
554#endif
555 return EINA_TRUE; 512 return EINA_TRUE;
556 } 513 }
557 514
@@ -570,7 +527,7 @@ end:
570 EO_OBJ_POINTER_PROXY(emb_obj_id, emb_obj); 527 EO_OBJ_POINTER_PROXY(emb_obj_id, emb_obj);
571 if (EINA_UNLIKELY(!emb_obj)) continue; 528 if (EINA_UNLIKELY(!emb_obj)) continue;
572 529
573 func = _vtable_func_get(EO_VTABLE(emb_obj), cache->op); 530 func = _vtable_func_get(EO_VTABLE(emb_obj), op);
574 if (func == NULL) goto composite_continue; 531 if (func == NULL) goto composite_continue;
575 532
576 if (EINA_LIKELY(func->func && func->src)) 533 if (EINA_LIKELY(func->func && func->src))
@@ -594,7 +551,7 @@ composite_continue:
594 if (cur_klass) 551 if (cur_klass)
595 { 552 {
596 ERR("in %s:%d: func '%s' (%d) could not be resolved for class '%s' for super of '%s'.", 553 ERR("in %s:%d: func '%s' (%d) could not be resolved for class '%s' for super of '%s'.",
597 file, line, func_name, cache->op, main_klass->desc->name, 554 file, line, func_name, op, main_klass->desc->name,
598 cur_klass->desc->name); 555 cur_klass->desc->name);
599 goto err; 556 goto err;
600 } 557 }
@@ -602,7 +559,7 @@ composite_continue:
602 { 559 {
603 /* we should not be able to take this branch */ 560 /* we should not be able to take this branch */
604 ERR("in %s:%d: func '%s' (%d) could not be resolved for class '%s'.", 561 ERR("in %s:%d: func '%s' (%d) could not be resolved for class '%s'.",
605 file, line, func_name, cache->op, main_klass->desc->name); 562 file, line, func_name, op, main_klass->desc->name);
606 goto err; 563 goto err;
607 } 564 }
608err_cache_op: 565err_cache_op:
@@ -612,7 +569,7 @@ err_cache_op:
612 goto err; 569 goto err;
613err_func_src: 570err_func_src:
614 ERR("in %s:%d: you called a pure virtual func '%s' (%d) of class '%s'.", 571 ERR("in %s:%d: you called a pure virtual func '%s' (%d) of class '%s'.",
615 file, line, func_name, cache->op, klass->desc->name); 572 file, line, func_name, op, klass->desc->name);
616err: 573err:
617 if (is_obj) 574 if (is_obj)
618 { 575 {
@@ -629,7 +586,7 @@ err:
629 // yes - special "move out of hot path" code blobs with goto's for 586 // yes - special "move out of hot path" code blobs with goto's for
630 // speed reasons to have intr prefetches work better and miss less 587 // speed reasons to have intr prefetches work better and miss less
631ok_cur_klass: 588ok_cur_klass:
632 func = _eo_kls_itr_next(klass, cur_klass, cache->op, super); 589 func = _eo_kls_itr_next(klass, cur_klass, op, super);
633 if (!func) goto end; 590 if (!func) goto end;
634 klass = func->src; 591 klass = func->src;
635 goto ok_cur_klass_back; 592 goto ok_cur_klass_back;
@@ -661,7 +618,6 @@ obj_super:
661 cur_klass = NULL; 618 cur_klass = NULL;
662 } 619 }
663 620
664 is_override = _obj_is_override(obj) && (cur_klass == NULL);
665 } 621 }
666 goto obj_super_back; 622 goto obj_super_back;
667 623