From 57ce6419e5c257e6fee6809cdb9c63d39c0b0a98 Mon Sep 17 00:00:00 2001 From: "Carsten Haitzler (Rasterman)" Date: Fri, 1 Apr 2016 20:29:04 +0900 Subject: [PATCH] e icons: reduce mem usage (in x11) by a fair bit by de-duplicating so i was profiling today .. leak hunting .. and i noticed. if you have enough appss open - eg terminology, e uses a huge amount of memory... for icons. terminology is 128x128 ... thats 64k per icon. open up a lot of terminology windows and we duplicate that 64k per every window on the wm sside because we get the data. it would apply for any app that sets a netwm icon. this can be come rather silly if you have like 100 terminals. it's worse with larger icons (eg 256x256 - 256k per icon). this puts in a simply list for shared icons and a lookup on fetch to de-duplicate and share icon data. this should drop memory usage nicely. @improvement --- src/bin/e_client.c | 103 +++++++++++++++++++++++++++++++++++++++++++-- src/bin/e_client.h | 2 + src/bin/e_comp_x.c | 22 ++-------- 3 files changed, 105 insertions(+), 22 deletions(-) diff --git a/src/bin/e_client.c b/src/bin/e_client.c index 1d2436697..e2a2bf652 100644 --- a/src/bin/e_client.c +++ b/src/bin/e_client.c @@ -534,10 +534,9 @@ _e_client_free(E_Client *ec) ec->stick_desks = eina_list_free(ec->stick_desks); if (ec->netwm.icons) { - int i; - for (i = 0; i < ec->netwm.num_icons; i++) - free(ec->netwm.icons[i].data); - E_FREE(ec->netwm.icons); + e_client_icon_free(ec->netwm.icons, ec->netwm.num_icons); + ec->netwm.icons = NULL; + ec->netwm.num_icons = 0; } E_FREE(ec->netwm.extra_types); eina_stringshare_replace(&ec->border.name, NULL); @@ -5023,3 +5022,99 @@ e_client_layout_cb_set(E_Client_Layout_Cb cb) CRI("ATTEMPTING TO OVERWRITE EXISTING CLIENT LAYOUT HOOK!!!"); _e_client_layout_cb = cb; } + +//////////////////////////////////////////// +static Eina_List *iconshare = NULL; + +typedef struct _E_Client_Icon_Entry E_Client_Icon_Entry; + +struct _E_Client_Icon_Entry +{ + Ecore_X_Icon *icons; + int num_icons; + int ref; +}; + +E_API Ecore_X_Icon * +e_client_icon_deduplicate(Ecore_X_Icon *icons, int num_icons) +{ + int i; + Eina_List *l; + E_Client_Icon_Entry *ie; + + // unless the rest of e uses border icons OTHER than icon #0 + // then free the rest that we don't need anymore. + for (i = 1; i < num_icons; i++) + { + free(icons[i].data); + icons[i].data = NULL; + } + // lookup icon data in icons cache/share + EINA_LIST_FOREACH(iconshare, l, ie) + { + if ((ie->num_icons == num_icons) && + (num_icons > 0) && + (ie->icons[0].width == icons[0].width) && + (ie->icons[0].height == icons[0].height) && + (!memcmp(ie->icons[0].data, icons[0].data, + icons[0].width * icons[0].height * 4))) + { + // found so free the input icons + for (i = 0; i < num_icons; i++) + free(icons[i].data); + free(icons); + // ref the shared/cached one + ie->ref++; + iconshare = eina_list_promote_list(iconshare, l); + // and return that + return ie->icons; + } + } + // no hit - new entry to cache. add it + ie = calloc(1, sizeof(E_Client_Icon_Entry)); + if (ie) + { + ie->icons = icons; + ie->num_icons = num_icons; + ie->ref = 1; + iconshare = eina_list_prepend(iconshare, ie); + } + return icons; +} + +E_API void +e_client_icon_free(Ecore_X_Icon *icons, int num_icons) +{ + int i; + Eina_List *l; + E_Client_Icon_Entry *ie; + + // lookup in icon share cache + EINA_LIST_FOREACH(iconshare, l, ie) + { + if ((ie->num_icons == num_icons) && + (num_icons > 0) && + (ie->icons[0].width == icons[0].width) && + (ie->icons[0].height == icons[0].height) && + (!memcmp(ie->icons[0].data, icons[0].data, + icons[0].width * icons[0].height * 4))) + { + // found so deref + ie->ref--; + if (ie->ref <= 0) + { + // no refs left - free the icon from the share/cache + iconshare = eina_list_remove_list(iconshare, l); + for (i = 0; i < ie->num_icons; i++) + free(ie->icons[i].data); + free(ie->icons); + free(ie); + } + return; + } + } + // not found - so just free it ... odd - we should never be here + for (i = 0; i < num_icons; i++) + free(icons[i].data); + free(icons); +} diff --git a/src/bin/e_client.h b/src/bin/e_client.h index 13d24fd56..94ef059af 100644 --- a/src/bin/e_client.h +++ b/src/bin/e_client.h @@ -827,6 +827,8 @@ E_API Eina_Bool e_client_has_xwindow(const E_Client *ec); E_API Eina_Bool e_client_desk_window_profile_available_check(E_Client *ec, const char *profile); E_API void e_client_desk_window_profile_wait_desk_set(E_Client *ec, E_Desk *desk); E_API void e_client_layout_cb_set(E_Client_Layout_Cb cb); +E_API Ecore_X_Icon *e_client_icon_deduplicate(Ecore_X_Icon *icons, int num_icons); +E_API void e_client_icon_free(Ecore_X_Icon *icons, int num_icons); YOLO E_API void e_client_focus_stack_set(Eina_List *l); diff --git a/src/bin/e_comp_x.c b/src/bin/e_comp_x.c index 35831a8da..2bff1106e 100644 --- a/src/bin/e_comp_x.c +++ b/src/bin/e_comp_x.c @@ -3795,30 +3795,16 @@ _e_comp_x_hook_client_fetch(void *d EINA_UNUSED, E_Client *ec) } if (ec->netwm.fetch.icon) { - int i; - if (ec->netwm.icons) - { - for (i = 0; i < ec->netwm.num_icons; i++) - { - free(ec->netwm.icons[i].data); - ec->netwm.icons[i].data = NULL; - } - free(ec->netwm.icons); - } + e_client_icon_free(ec->netwm.icons, ec->netwm.num_icons); ec->netwm.icons = NULL; ec->netwm.num_icons = 0; if (ecore_x_netwm_icons_get(win, &ec->netwm.icons, &ec->netwm.num_icons)) { - // unless the rest of e uses border icons OTHER than icon #0 - // then free the rest that we don't need anymore. - for (i = 1; i < ec->netwm.num_icons; i++) - { - free(ec->netwm.icons[i].data); - ec->netwm.icons[i].data = NULL; - } - ec->netwm.num_icons = 1; + if (ec->netwm.icons) + ec->netwm.icons = e_client_icon_deduplicate + (ec->netwm.icons, ec->netwm.num_icons); ec->changes.icon = 1; } ec->netwm.fetch.icon = 0;