Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 269248

Summary: [Viewers] TreeViewer sort takes an inordinate amount of time
Product: [Eclipse Project] Platform Reporter: James Blackburn <jamesblackburn+eclipse>
Component: SWTAssignee: Bogdan Gheorghe <gheorghe>
Status: CLOSED WONTFIX QA Contact:
Severity: normal    
Priority: P3 CC: bokowski, eclipse.felipe, ericwill, gautier.desaintmartinlacaze, gheorghe, snorthov, yevshif
Version: 3.4Keywords: triaged
Target Milestone: ---   
Hardware: PC   
OS: Linux-GTK   
Whiteboard: stalebug
Bug Depends on:    
Bug Blocks: 519166    
Attachments:
Description Flags
Example Test
none
Win32 performance test output
none
GTK test output
none
test 2 none

Description James Blackburn CLA 2009-03-18 13:23:03 EDT
I'm seeing a sort in a TreeViewer wrapped Tree control take an inordinate amount of time (0.4s per element).  In this case the tree viewer contains a flat table of data (I'm using TreeViewer because it sometimes contains nested data).

My tree has multiple columns (~10) and I've added a selectionListener to each TreeColumn in the usual way.  I've #setSorter on the TreeViewer to use my own custom sorter.  When the column is clicked, I #update() or #refresh() the tree viewer for the sort to take place.  

Inserting some print's I can see that the ViewerSorter#sort(...) takes 57ms to sort ~15000 elements, but the viewer update / refresh takes 6.5s. It doesn't matter whether I do #update or #refresh... 

Using:

TreeViewer#update():
Sample ViewerSorter#sort (14803 elements) took: 51
Viewer #update (14803 items) took: 6623

TreeViewer#refresh():
Sample ViewerSorter#sort (14803 elements) took: 51
Viewer #update (14803 items) took: 6623

The code that produced this looks like this:

// Listener for the sorter
SelectionListener headerListener = new SelectionAdapter() {
	public void widgetSelected(SelectionEvent se) {
		int icol = _viewer.getTree().indexOf((TreeColumn) se.widget);
		TreeColumn col = _viewer.getTree().getColumns()[icol];
		IMetricDisplay md = ...;
		SampleSorter sorter = (SampleSorter) _viewer.getSorter();
		if (md == sorter.getColumn())
			sorter.setReversed(!sorter.isReversed());
		else
			sorter.setColumn(md);

		_viewer.getTree().setSortColumn(col);
		_viewer.getTree().setSortDirection(sorter.isReversed() ? SWT.DOWN : SWT.UP);
		long start = System.currentTimeMillis();
//				_viewer.update(_viewer.getTree().getData(), new String[] {SampleSorter.SORT_REQUIRED});
		_viewer.refresh(false);
		if (true)
			System.out.println("Viewer #update ("+ _viewer.getTree().getItemCount() + " items) took: " + (System.currentTimeMillis() - start));
	}
};
column.addSelectionListener(headerListener);

And the sorter has wrapped #sort:
public void sort(Viewer viewer, Object[] elements) {
	long start = System.currentTimeMillis();
	super.sort(viewer, elements);
	if (DEBUG)
		System.out.println("Sample ViewerSorter#sort ("+ elements.length + " elements) took: " + (System.currentTimeMillis() - start));
}


I was under the impression that #update on the JFace TreeViewer would be relatively efficient if the domain objects haven't changed: http://www.eclipse.org/articles/Article-TreeViewer/TreeViewerArticle.htm

Nonetheless, having scoured google and the existing example docs (there isn't much on tree viewer sorting), I'm stumped as to why sorting in the viewer takes 0.4s per element...

Versions:
JFace: org.eclipse.jface_3.4.0.I20080606-1300.jar
GTK: 2.12.11
Comment 1 James Blackburn CLA 2009-03-18 13:29:36 EDT
(In reply to comment #0)
> Using:
> 
> TreeViewer#update():
> Sample ViewerSorter#sort (14803 elements) took: 51
> Viewer #update (14803 items) took: 6623
> 
> TreeViewer#refresh():
> Sample ViewerSorter#sort (14803 elements) took: 51
> Viewer #update (14803 items) took: 6623

These numbers aren't meant to be identical, but they're pretty close... Running it a few times I get pretty consistent results:

#refresh:
Sample ViewerSorter#sort (14803 elements) took: 61
Viewer update (14803 items) took: 6615
Sample ViewerSorter#sort (14803 elements) took: 51
Viewer update (14803 items) took: 6550
Sample ViewerSorter#sort (14803 elements) took: 51
Viewer update (14803 items) took: 6530
Sample ViewerSorter#sort (14803 elements) took: 51
Viewer update (14803 items) took: 6538

#update:
Sample ViewerSorter#sort (14803 elements) took: 52
Viewer update (14803 items) took: 6567
Sample ViewerSorter#sort (14803 elements) took: 51
Viewer update (14803 items) took: 6569
Sample ViewerSorter#sort (14803 elements) took: 50
Viewer update (14803 items) took: 7130
Sample ViewerSorter#sort (14803 elements) took: 52
Viewer update (14803 items) took: 6594
Comment 2 Felipe Heidrich CLA 2009-03-18 16:54:41 EDT
Please provide a full example I can run here.


UI Team, is that a known problem ?
Comment 3 James Blackburn CLA 2009-03-18 17:16:46 EDT
(In reply to comment #2)
> Please provide a full example I can run here.
> 
> UI Team, is that a known problem ?

Will do. I've been tracing through the code a bit to see if I can find anything obvious... Nothing so far. I've bumped to jface HEAD, and tried a couple versions of gtk. Will try to put a test together.
Comment 4 James Blackburn CLA 2009-03-18 19:10:49 EDT
Created attachment 129303 [details]
Example Test

Attached is an example of how I'm using the API.  It's a patch creating a test class in org.eclipse.jface.tests.performance. I've not used the performance testing infrastructure before, so in this example, it's just printing absolute times taken to the console.

The numbers I'm seeing using the simple TreeTest infrastructure appear broadly similar to my app.  I've plotted the numbers and the #sort itself scales O(nlgn) whereas the overall #refresh / #update looks to be O(n^2)(?).


Elements	Time ms (#Sort)	Time ms (#refresh / #update)	
		
5000	111	650
5000	320	2323
15000		4318
20000	593	7365
30000	846	16858
50000	1665	121307

In my app I've got a few more columns, so my overall update / refresh is slower, but my sorter is faster (I'm avoiding string comparisons).

Clearly I've got a lot of elements in the viewer, but the point is once I've built my model the view is pretty static (I don't update the labels or structure); the model is built on a non-UI thread.  As the sort itself is quick, I'd like the update of the view to be similarly scalable...
Comment 5 Boris Bokowski CLA 2009-04-16 10:18:10 EDT
This bug was still assigned to the SWT inbox.

James, have you tried using setRedraw? Something like:

_viewer.getTree().setRedraw(false);
try {
  // ... change sorting / filtering etc.
} finally {
  _viewer.getTree().setRedraw(true);
}
Comment 6 James Blackburn CLA 2009-04-16 10:56:48 EDT
(In reply to comment #5)
> This bug was still assigned to the SWT inbox.
> 
> James, have you tried using setRedraw? Something like:

Thank Boris, unfortunately it seems to make little / no difference :(

I did some similar timings on my laptop (which runs os x cocoa) which seemed to scale more linearly with the number of items.  Which suggests something odd happening in SWT GTK or perhaps GTK itself.  

Fundamentally it must be broken that, under a pretty beefy machine, it takes 0.5s for each tree view when there are 14k items... 
Comment 7 James Blackburn CLA 2009-04-16 10:57:47 EDT
(In reply to comment #6)
> it takes 0.5s for each tree view _item_ when there are 14k items... 

Comment 8 Boris Bokowski CLA 2009-04-20 23:57:25 EDT
Is viewer.setUseHashLookup(true) being called by the superclass? (Sorry if this is a stupid question but I don't have an Eclipse open right now, just looking at the attached code.)
Comment 9 James Blackburn CLA 2009-04-22 03:59:26 EDT
(In reply to comment #8)
> Is viewer.setUseHashLookup(true) being called by the superclass? (Sorry if this
> is a stupid question but I don't have an Eclipse open right now, just looking
> at the attached code.)

In my app I've tried with an without #setUseHashLookup(true). It doesn't seem to make any difference.

I found a page of SWT/GTK+ performance notes: http://vektor.ca/eclipse/gtk-performance-notes.html
which states performance for large gtk trees is poor (compared with other widget toolkits). It claims one of the reasons is that rows dont' have fixed height by default and states:
"GTK+ version 2.4 introduced a new "fixed-height-mode" property for tree views, but it has some limitations and is not used in their combo box implementation. This property is used if available by the SWT implementation of SWT.VIRTUAL for Trees, and maybe we can use it in more situations."


Comment 10 Boris Bokowski CLA 2009-04-27 22:43:07 EDT
(In reply to comment #6)
> I did some similar timings on my laptop (which runs os x cocoa) which seemed to
> scale more linearly with the number of items.  Which suggests something odd
> happening in SWT GTK or perhaps GTK itself.  
> 
> Fundamentally it must be broken that, under a pretty beefy machine, it takes
> 0.5s for each tree view when there are 14k items... 

Moving back to SWT. I agree that if this runs well on Cocoa but not on GTK, it must be SWT or GTK itself.
Comment 11 Steve Northover CLA 2009-04-29 16:24:49 EDT
BG to investigate.
Comment 12 Bogdan Gheorghe CLA 2009-05-11 13:11:31 EDT
Using the M7 build, I got the JFace Performance tests running on both my Win32 box and my Solaris x86 box (which is my fastest GTK box and slightly faster than the Win32)- but both are Core 2 Duos with 2Gb RAM. I patched the performance suite with James' attached test case and ran just the two test cases. 

My findings were that the refresh/update tests run in about the same amount of time on both machines (I'll attach the complete trace for both platforms):

Win32
=====
Sort (avg.): 953 ms
Viewer refresh: 2875 ms

Sort (avg.): 954 ms
Viewer update: 2864 ms

GTK 2.14.3
===========

Sort (avg.): 381 ms
Viewer refresh: 2937ms

Sort (avg.): 551 ms
Viewer update: 3076 ms
Comment 13 Bogdan Gheorghe CLA 2009-05-11 13:12:08 EDT
Created attachment 135177 [details]
Win32 performance test output
Comment 14 Bogdan Gheorghe CLA 2009-05-11 13:20:22 EDT
Created attachment 135178 [details]
GTK test output
Comment 15 Bogdan Gheorghe CLA 2009-05-11 13:25:27 EDT
Given the similar test results, I'm not sure we can say that the timings indicate a GTK implementation problem.

I'm removing this from the RC1 list, but I'm not sure if this should be returned to JFace or closed. Boris?
Comment 16 James Blackburn CLA 2009-05-11 16:02:17 EDT
Created attachment 135208 [details]
test 2

(In reply to comment #15)
> Given the similar test results, I'm not sure we can say that the timings
> indicate a GTK implementation problem.

Hmm I'm not sure one data point is enough... Attached is a slightly improved test which runs a few more iterations with increasing numbers of items. 

Cocoa looks reasonably linear, are you seeing a similar spread on your Windows & Solaris machines?

testTreeSortRefresh: setUp...
Viewer refresh (1024 items) sort: 111ms     refresh total: 489ms
Viewer refresh (2048 items) sort: 265ms     refresh total: 964ms
Viewer refresh (4096 items) sort: 767ms     refresh total: 2988ms
Viewer refresh (8192 items) sort: 1674ms     refresh total: 5509ms
Viewer refresh (16384 items) sort: 3423ms     refresh total: 10137ms
Comment 17 Bogdan Gheorghe CLA 2009-05-11 17:20:15 EDT
Win32:
Viewer refresh (1024 items) sort: 36ms     refresh total: 177ms
Viewer refresh (2048 items) sort: 78ms     refresh total: 349ms
Viewer refresh (4096 items) sort: 208ms     refresh total: 755ms
Viewer refresh (8192 items) sort: 469ms     refresh total: 1573ms
Viewer refresh (16384 items) sort: 1151ms     refresh total: 3557ms
Viewer refresh (32768 items) sort: 2250ms     refresh total: 6562ms

Viewer update (1024 items) sort: 36ms     update total: 171ms
Viewer update (2048 items) sort: 83ms     update total: 359ms
Viewer update (4096 items) sort: 192ms     update total: 729ms
Viewer update (8192 items) sort: 442ms     update total: 1588ms
Viewer update (16384 items) sort: 1119ms     update total: 3447ms
Viewer update (32768 items) sort: 2270ms     update total: 6838ms
Comment 18 Bogdan Gheorghe CLA 2009-05-11 17:29:20 EDT
GTK:

Viewer refresh (1024 items) sort: 27ms     refresh total: 166ms
Viewer refresh (2048 items) sort: 32ms     refresh total: 190ms
Viewer refresh (4096 items) sort: 77ms     refresh total: 419ms
Viewer refresh (8192 items) sort: 178ms     refresh total: 1119ms
Viewer refresh (16384 items) sort: 412ms     refresh total: 3372ms
Viewer refresh (32768 items) sort: 886ms     refresh total: 11078ms


Viewer update (1024 items) sort: 77ms     update total: 135ms
Viewer update (2048 items) sort: 189ms     update total: 320ms
Viewer update (4096 items) sort: 110ms     update total: 442ms
Viewer update (8192 items) sort: 252ms     update total: 1183ms
Viewer update (16384 items) sort: 607ms     update total: 3548ms
Viewer update (32768 items) sort: 1304ms     update total: 11518ms

Comment 19 Bogdan Gheorghe CLA 2009-05-11 17:30:28 EDT
GTK gets a bit long on the last case, but not too bad otherwise.
Comment 20 James Blackburn CLA 2009-05-11 18:22:17 EDT
(In reply to comment #19)
> GTK gets a bit long on the last case, but not too bad otherwise.

But that's my point. It doesn't scale O(n) like it does Windows & Mac OS.  In an app with multiple columns, the actual upper pain threshold is smaller -- 15k items or fewer for us. This wouldn't be the case if the SWT/GTK treeview scaled like it does on windows.
Comment 21 Eric Williams CLA 2018-11-30 11:30:45 EST
Is there a pure SWT snippet for this ticket?
Comment 22 Eclipse Genie CLA 2020-11-22 15:33:01 EST
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet. As such, we're closing this bug.

If you have further information on the current state of the bug, please add it and reopen this bug. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.