ListView excessive re-binding

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_MAX_LIST_ITEMS and 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

Yes

A listview is a GtkScrollable, so it knows the size of the scrolledwindow via its GtkAdjustment(s).

Yes.

GTK_LIST_VIEW_MAX_LIST_ITEMS: the name suggests that this is a hard maximum, not the default value. Is this a “one size fits all” approach due to the possibility of rows with varying height? 200 seems odd to me as it is overkill for most applications on common displays and probably still not enough for the most extreme edge cases.

Do you have a suggestion how I can improve my code or how ListView itself can be improved to avoid the mass re-binding? My ListView rows all have the exact same height. I guess because this is not true for all lists, a lot of optimizations could not be applied in the ListView code?
Are my two rough ideas mentioned above even feasible?

Well it’s not the default. If your model had fewer than 200 rows, the listview would create fewer than 200 row widgets.

No. If this really takes so long, I’d suggest to just write a small reproducer and open a bug report. Maybe Benjamin will even read it.