I’m trying to solve a performance issue I’m facing with the new ListView of Gtk4. Maybe someone can help me to understand why things are happening the way they do.
A common case for NewsFlash is a long list with a few hundred items. Upon a refresh a bunch more items (~20-100) are added to the top of the list. This causes the application to freeze for a few seconds depending on the size of the list and how many items are added to the top.
Investigating the issue I found that the ListView Factory binds a lot of model items to a ListItem. So I toyed around with the second list_view example of the Rust Gtk4 book: https://github.com/gtk-rs/gtk4-rs/tree/master/book/listings/list_widgets/2
It doesn’t matter how small I make the height of the window (lowered to 200) or how large I make the height of the ListView rows (increased to 50). Upon startup the factory binds 204 of 1_000_000 model items to rows when barely 4 are going to be visible.
Every time a new model item is inserted at pos 0 of the list-model, the first 204 items get re-bound again. In the case of adding 100 new items to the top of the list this means re-binding 204 items x100.
So if the binding process is a lot more expensive than just setting a single label things can go out of hand quickly.
What I don’t understand is how the ListView and its parent ScrolledWindow know of each other. Does the ListView simply expect its parent to be a ScrolledWindow?
Because the ListView somehow needs to know the height of the ScrolledWindow and its scroll position to calculate which items are going to be visible and need to be bound to the recyclable rows. It seems to be working. Otherwise I would expect all 1_000_000 items to be rebound every time.
What is also weird is that both in my complex article-list in NewsFlash as well as the minimal sample I end up with exactly 204 items bound every time. No matter the height of the rows and the parent ScrolledWindow.
Is this due to
GTK_LIST_VIEW_EXTRA_ITEMS being used instead of more accurate values?
I see two possible paths of improving the situation:
- have listview only re-bind a number of rows closer to what is actually visible on screen each new insert
- a way to do atomic updates to a ListStore/ListModel and only then re-evaluate what needs to be re-bound in the ListView
Ideally both things could be applied at the same time.
Could the second idea be achieved by a custom GListModel? As far as I understand the
items-changed signal needs to be emitted for every changed item anyway, which would trigger the re-binding of 204 rows each time again.
Most of the testing happened with Gtk 4.4.1. But I am facing the general performance issue with 4.5.0 as well.
Thanks for any input