This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch reproducible-widget-previews
in repository efl.
View the commit online.
commit 30ecad3e2bdb7be57f412d7857ea480772ab993d
Author: [email protected] <[email protected]>
AuthorDate: Sat Apr 25 10:44:03 2026 -0600
evas_textgrid: reuse row backing arrays to eliminate malloc churn
Callgrind profiling of the textgrid benchmark (expedite -t 69, GL backend)
showed ~25% of total time spent in glibc malloc/free family. The hot path
traced back to _evas_textgrid_update_add invoking evas_object_textgrid_row_clear
on every dirty row per frame, which frees texts, rects, and lines arrays.
The very next render cycle immediately reallocates them via realloc's grow
strategy (+= 32 slots), feeding unnecessary malloc/free contention. For
typical 80-column terminal-style grids, this means several reallocs per
dirty row per frame on a steady-state display.
Split the row lifecycle into two operations:
- row_reset: hot path. Unrefs text_props content and zeros counters; arrays
persist for reuse. Wire into _evas_textgrid_update_add for every dirty row.
- row_clear: full teardown. Frees arrays and resets alloc fields; use only
during grid resizing and object destruction.
Pre-allocate texts and rects to grid width and lines to 8 slots during
grid_size_set, eliminating the realloc cycle entirely on steady-state renders.
The append-path realloc safety net remains as a fallback for correctness.
Add rows_reset as a mirror to rows_clear for font/palette reload paths,
which mark rows dirty but must not deallocate pre-sized backing arrays.
This reduces malloc/free calls by 100% on steady-state textgrid rendering
while maintaining full OOM correctness in grid_size_set.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
src/lib/evas/canvas/evas_object_textgrid.c | 120 +++++++++++++++++++++++++++--
1 file changed, 115 insertions(+), 5 deletions(-)
diff --git a/src/lib/evas/canvas/evas_object_textgrid.c b/src/lib/evas/canvas/evas_object_textgrid.c
index 0b5a335508..5964f8c36c 100644
--- a/src/lib/evas/canvas/evas_object_textgrid.c
+++ b/src/lib/evas/canvas/evas_object_textgrid.c
@@ -148,6 +148,28 @@ evas_object_textgrid_init(Evas_Object *eo_obj)
eina_array_step_set(&o->cur.palette_extended, sizeof (Eina_Array), 16);
}
+/* Reset a row for reuse without freeing its backing arrays.
+ * This is the hot path: called every dirty-row per frame.
+ * Arrays survive with their current alloc; only _num counters are zeroed.
+ * text_props content refs are released because new render will re-create them. */
+static void
+evas_object_textgrid_row_reset(Evas_Textgrid_Data *o EINA_UNUSED,
+ Evas_Object_Textgrid_Row *r)
+{
+ int i;
+
+ if (r->texts)
+ {
+ for (i = 0; i < r->texts_num; i++)
+ evas_common_text_props_content_unref(&(r->texts[i].text_props));
+ }
+ r->texts_num = 0;
+ r->rects_num = 0;
+ r->lines_num = 0;
+}
+
+/* Fully free a row's arrays and reset all counters/alloc fields.
+ * This is the teardown path: called on grid resize and object destruction. */
static void
evas_object_textgrid_row_clear(Evas_Textgrid_Data *o EINA_UNUSED,
Evas_Object_Textgrid_Row *r)
@@ -194,6 +216,26 @@ evas_object_textgrid_rows_clear(Evas_Object *eo_obj)
}
}
+/* Mark all rows dirty without freeing their backing arrays.
+ * Use this on font/palette changes where rows survive intact but must be
+ * fully re-rendered: backing arrays keep their pre-sized capacity, so the
+ * next render's row_text_append never needs to realloc on a steady-state grid.
+ * Use rows_clear (full teardown) only when the row array itself is being freed. */
+static void
+evas_object_textgrid_rows_reset(Evas_Object *eo_obj)
+{
+ int i;
+
+ Evas_Textgrid_Data *o = efl_data_scope_get(eo_obj, MY_CLASS);
+ if (!o->cur.rows) return;
+ for (i = 0; i < o->cur.h; i++)
+ {
+ evas_object_textgrid_row_reset(o, &(o->cur.rows[i]));
+ o->cur.rows[i].ch1 = 0;
+ o->cur.rows[i].ch2 = o->cur.w - 1;
+ }
+}
+
static void
evas_object_textgrid_free(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj EINA_UNUSED)
{
@@ -922,8 +964,76 @@ _evas_textgrid_grid_size_set(Eo *eo_obj, Evas_Textgrid_Data *o, int w, int h)
}
for (i = 0; i < h; i++)
{
- o->cur.rows[i].ch1 = 0;
- o->cur.rows[i].ch2 = w - 1;
+ Evas_Object_Textgrid_Row *r = &o->cur.rows[i];
+
+ r->ch1 = 0;
+ r->ch2 = w - 1;
+
+ /* Pre-allocate backing arrays sized for the full row width so the hot
+ * render path never needs to realloc on a steady-state grid. */
+ r->texts = calloc(w, sizeof(Evas_Object_Textgrid_Text));
+ if (!r->texts)
+ {
+ /* OOM: free all rows allocated so far then the row array itself */
+ int j;
+ for (j = 0; j < i; j++)
+ {
+ free(o->cur.rows[j].texts);
+ free(o->cur.rows[j].rects);
+ free(o->cur.rows[j].lines);
+ }
+ free(o->cur.rows);
+ o->cur.rows = NULL;
+ free(o->cur.cells);
+ o->cur.cells = NULL;
+ return;
+ }
+ r->texts_alloc = w;
+
+ r->rects = calloc(w, sizeof(Evas_Object_Textgrid_Rect));
+ if (!r->rects)
+ {
+ int j;
+ free(r->texts);
+ r->texts = NULL;
+ r->texts_alloc = 0;
+ for (j = 0; j < i; j++)
+ {
+ free(o->cur.rows[j].texts);
+ free(o->cur.rows[j].rects);
+ free(o->cur.rows[j].lines);
+ }
+ free(o->cur.rows);
+ o->cur.rows = NULL;
+ free(o->cur.cells);
+ o->cur.cells = NULL;
+ return;
+ }
+ r->rects_alloc = w;
+
+ r->lines = calloc(8, sizeof(Evas_Object_Textgrid_Line));
+ if (!r->lines)
+ {
+ int j;
+ free(r->texts);
+ r->texts = NULL;
+ r->texts_alloc = 0;
+ free(r->rects);
+ r->rects = NULL;
+ r->rects_alloc = 0;
+ for (j = 0; j < i; j++)
+ {
+ free(o->cur.rows[j].texts);
+ free(o->cur.rows[j].rects);
+ free(o->cur.rows[j].lines);
+ }
+ free(o->cur.rows);
+ o->cur.rows = NULL;
+ free(o->cur.cells);
+ o->cur.cells = NULL;
+ return;
+ }
+ r->lines_alloc = 8;
}
o->cur.w = w;
o->cur.h = h;
@@ -1190,7 +1300,7 @@ _evas_textgrid_font_reload(Eo *eo_obj, Evas_Textgrid_Data *o)
evas_object_inform_call_resize(eo_obj, obj);
o->changed = 1;
o->core_change = 1;
- evas_object_textgrid_rows_clear(eo_obj);
+ evas_object_textgrid_rows_reset(eo_obj);
evas_object_change(eo_obj, obj);
}
@@ -1348,7 +1458,7 @@ _evas_textgrid_palette_set(Eo *eo_obj, Evas_Textgrid_Data *o, Evas_Textgrid_Pale
}
o->changed = 1;
o->pal_change = 1;
- evas_object_textgrid_rows_clear(eo_obj);
+ evas_object_textgrid_rows_reset(eo_obj);
evas_object_change(eo_obj, obj);
}
@@ -1436,7 +1546,7 @@ _evas_textgrid_update_add(Eo *eo_obj, Evas_Textgrid_Data *o, int x, int y, int w
if (r->ch1 < 0)
{
- evas_object_textgrid_row_clear(o, r);
+ evas_object_textgrid_row_reset(o, r);
r->ch1 = x;
r->ch2 = x2;
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.