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.

Reply via email to