SrainApp / srain

Modern IRC client written in GTK
https://srain.silverrainz.me/
Other
299 stars 34 forks source link

GtkList performance: Excessively deep stack during widget render #262

Open kennylevinsen opened 3 years ago

kennylevinsen commented 3 years ago

Here is a backtrace from GDB while analyzing some significant performance issues:

#0  gtk_css_animated_style_is_static (style=<optimized out>) at ../gtk/gtk/gtkcssanimatedstyle.c:74
#1  0x00007f01befba24c in gtk_css_style_is_static (style=0x558275555000) at ../gtk/gtk/gtkcssstyle.c:115
#2  gtk_css_node_real_update_style (cssnode=0x558272d12580, change=<optimized out>, timestamp=6817218791, style=<optimized out>) at ../gtk/gtk/gtkcssnode.c:453
#3  0x00007f01befbf27f in gtk_css_node_ensure_style.part.0.lto_priv.0 (cssnode=0x558272d12580, current_time=current_time@entry=6817218791) at ../gtk/gtk/gtkcssnode.c:1007
#4  0x00007f01befbf248 in gtk_css_node_ensure_style (current_time=6817218791, cssnode=<optimized out>) at ../gtk/gtk/gtkcssnode.c:1003
#5  gtk_css_node_ensure_style.part.0.lto_priv.0 (cssnode=0x558272d1e0a0, current_time=current_time@entry=6817218791) at ../gtk/gtk/gtkcssnode.c:1003
#6  0x00007f01befbf248 in gtk_css_node_ensure_style (current_time=6817218791, cssnode=<optimized out>) at ../gtk/gtk/gtkcssnode.c:1003
#7  gtk_css_node_ensure_style.part.0.lto_priv.0 (cssnode=0x558272d1e5a0, current_time=current_time@entry=6817218791) at ../gtk/gtk/gtkcssnode.c:1003

... snip, gtk_css_node_ensure_style repeats a thousand times ...

#2558 0x00007f01befbf248 in gtk_css_node_ensure_style (current_time=6817218791, cssnode=<optimized out>) at ../gtk/gtk/gtkcssnode.c:1003
#2559 gtk_css_node_ensure_style.part.0.lto_priv.0 (cssnode=0x558275fb38a0, current_time=6817218791) at ../gtk/gtk/gtkcssnode.c:1003
#2560 0x00007f01befb78be in gtk_css_node_ensure_style (current_time=<optimized out>, cssnode=0x558275fb38a0) at ../gtk/gtk/gtkcssnode.c:1033
#2561 gtk_css_node_get_style (cssnode=0x558275fb38a0) at ../gtk/gtk/gtkcssnode.c:1033
#2562 0x00007f01bf1e3b15 in gtk_style_context_lookup_style (context=<optimized out>) at ../gtk/gtk/gtkstylecontext.c:1619
#2563 _gtk_style_context_peek_property (property_id=78, context=<optimized out>) at ../gtk/gtk/gtkstylecontext.c:1619
#2564 gtk_widget_update_alpha (widget=0x558275fb51c0) at ../gtk/gtk/gtkwidget.c:16094
#2565 0x00007f01bf1d5476 in gtk_widget_realize (widget=0x558275fb51c0) at ../gtk/gtk/gtkwidget.c:5537
#2566 0x00007f01bf1da2a9 in gtk_widget_set_parent (widget=0x558275fb51c0, parent=0x558269c39a20) at ../gtk/gtk/gtkwidget.c:9659
#2567 0x00007f01bf079027 in gtk_list_box_insert (box=0x558269c39a20, child=<optimized out>, position=-1) at ../gtk/gtk/gtklistbox.c:2958
#2568 0x000055826759d906 in sui_message_list_append_message (self=0x55826addbd20, msg=<optimized out>, halign=<optimized out>) at sui/sui_message_list.c:178
#2569 0x000055826759dab5 in sui_message_list_add_message (self=<optimized out>, msg=<optimized out>, halign=<optimized out>) at sui/sui_message_list.c:217
#2570 0x0000558267595d99 in sui_buffer_add_message (buf=0x55826ac38e80, msg=0x558275f996f0) at sui/sui.c:144
#2571 0x00005582675834bc in add_message (self=0x55826accee60, msg=0x558275c2de50) at core/chat.c:472
#2572 0x000055826758fcac in on_recv_ready (obj=<optimized out>, res=<optimized out>, user_data=0x55826aa83bb0) at sirc/sirc.c:242
#2573 0x00007f01beaf88cc in  () at /usr/lib/libgio-2.0.so.0
#2574 0x00007f01beb29844 in  () at /usr/lib/libgio-2.0.so.0
#2575 0x00007f01beb29879 in  () at /usr/lib/libgio-2.0.so.0
#2576 0x00007f01be95a340 in g_main_context_dispatch () at /usr/lib/libglib-2.0.so.0
#2577 0x00007f01be9a81d9 in  () at /usr/lib/libglib-2.0.so.0
#2578 0x00007f01be959221 in g_main_context_iteration () at /usr/lib/libglib-2.0.so.0
#2579 0x00007f01beb55c9e in g_application_run () at /usr/lib/libgio-2.0.so.0
#2580 0x000055826757a0ed in main (argc=1, argv=0x7fffb8804318) at core/srain.c:56

This call stack is 2580 frames deep, and appears caused by recursively visiting sibling nodes for which there are many. This ends up running for every

I imagine this is caused by a bad widget layout or unfortunate CSS, but I haven't analyzed it further. Tested on 1.1.2.

kennylevinsen commented 3 years ago

Inspecting the layout, it would appear that there are two problems:

  1. Each SuiRecvMessage is comprised of many widgets (several layers of boxes).
  2. Each buffer maintains a GtkListBox containing all messages that have been received as shown widgets.
  3. A window contains all buffers within a GtkStack, so that even if they are not rendered, the widgets are still retained.

The end-result is that there are N M k widgets, where N is the number of buffers, M is the number of messages, and k is the number of widgets per message. This very quickly gets out of control, leading to terrible performance: Srain is unresponsive for a long period of time when connecting to my bouncer, has long delays when animating the "got focus"/"focus lost" animation, and is also pretty unusable when the Gtk debugger is active.

Possible solutions:

  1. This is not necessarily a problem on its own, but changes here is quickly amplified, so removing e.g. a GtkBox layer could be beneficial.
  2. This is the biggest problem, and a fundamental flaw in GtkList. Use of Gtk4 (or rather, 3.99) will solve this through the new widget-recycling list views. Under Gtk3, one can alternatively hide older/new entries through a "click to see more" pagination.
  3. Avoiding GtkStack and instead recycling the UI widgets (message list, user list, ...) by updating their content on buffer change would significantly reduce the widget count and thus resource utilization (although not help with CSS performance that much).

Number 3 can already be implemented, and so can number 1 if there are any ideas for simplification. Number 2 would be part of a Gtk4 port (and, with 3.99 out, it's time to play with that anyway).

SilverRainZ commented 3 years ago

Thanks for pointing out this.

The simplest way may be take a taste of GTK4, I will have a try when I have time.

SilverRainZ commented 3 years ago

As GTK4 is released, we are starting to migrate to GTK4 :)

SilverRainZ commented 2 years ago

346 inctroduces a clear command, may be helpful to this.