TableView with many columns poor ui performance
danny.gonzalez at screamingfrog.co.uk
Thu Feb 6 16:27:05 UTC 2020
I have submitted a pull request and the branch is here:
On 6 Feb 2020, at 09:50, Ed Kennard <ed at kennard.net<mailto:ed at kennard.net>> wrote:
This is great news, I've previously written about perf issues with TableView, do you have a branch I could try out which is rebased off current jfx master?
On 06/02/2020, 10:40, "openjfx-dev on behalf of Danny Gonzalez" <openjfx-dev-bounces at openjdk.java.net<mailto:openjfx-dev-bounces at openjdk.java.net> on behalf of danny.gonzalez at screamingfrog.co.uk<mailto:danny.gonzalez at screamingfrog.co.uk>> wrote:
We have been struggling with our migration from Java 8 to Java 11 due to various issues we are having with TableView. The main issue is the general UI lag and slow TableView scroll performance.
For context here some links around the issues:
TableView has a horrific performance with many columns #409
JDK-8088394 : Huge memory consumption in TableView with too many columns
JavaFX TreeTableView slow scroll performance
TableRowSkinBase fails to correctly virtualise cells in horizontal direction
OpenJFX mailing list thread: TableView slow vertical scrolling with 300+ columns
We have a TableView that is being populated with rows dynamically around the rate of 2 rows per second. The issue we are experiencing is the lagginess of the UI when these rows are being inserted. TableViews with fewer columns don’t seem to exhibit the behaviour quite as badly.
I can see that there was an optimisation in Java 8 that would alleviate the TableView performance issue. If you used a fixed cell size in your TableView, a bit of optimisation code in TableRowSkinBase.isColumnPartiallyOrFullyVisible would only render the visible columns. This code, as has been documented in the first link above, used to work in Java 8 but not now in Java 11. It also has a bug where the visible columns calculation is incorrect and instead calculates the visibility on the whole width of the TableView rather than the visible columns. I have tried fixing this up, and even with this code fixed, it brings in other issues where resizing the TableView results in blank columns.
I have done some testing and can see that the main issues with the slow down is to do with the thousands of listeners that are being removed in TableView when you add rows or scroll. It is taking up to 60% of the JavaFX Application thread.
For whatever reason the amount of listeners registering/unregistering has jumped significantly between Java 8 and Java 11.
The following changeset for example (which added an extra listener on the Node class) impacted TableView performance significantly:
Author: Florian Kirmaier <fk at sandec.de<mailto:fk at sandec.de><mailto:fk at sandec.de>>
Date: Tue Jan 22 09:08:36 2019 -0800
8216377: Fix memoryleak for initial nodes of Window
8207837: Indeterminate ProgressBar does not animate if content is added after scene is set on window
Add or remove the windowShowingChangedListener when the scene changes
I can see that the code for removing listeners in ExpressionHelper.Generic is extremely sub optimal and uses a linear search through an array to remove listeners.
I have rewritten this class to use a Set instead of an Array and it has fixed our major TableView issues where the performance of the UI seriously deteriorated to the point of not being usable. The CPU usage of com.sun.javafx.binding.ExpressionHelper.removeListener in the JavaFX Application Thread has dropped from 60% to less than 1%.
I believe this fix will alleviate the major issues of TableViews detailed in the above links.
I have run my fix through the unit test suite and believe it is functionally equivalent to the original code, just much faster.
I am happy to submit a pull request. I am new to openJFX contributing so any pointers of what I need to do here would be appreciated.
More information about the openjfx-dev