Skip to content

TreeItem.getItemCount() and TreeItem.getItem(int) have linear complexity on Linux #882

@basilevs

Description

@basilevs

The org.eclipse.swt.widgets.Tree is slow on Linux. In particular TreeItem.getItemCount() and TreeItem.getItem(int) have linear complexity (the time of execution is proportional to the number of children).

To Reproduce

Run JUnit test or following snippet:

import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;

public class TreeTest {

	private Shell shell = new Shell();
	{
		shell.setLayout(new FillLayout());
		shell.open();
	}


	public void getItemShouldHaveConstantTime() {
		for (int i = 0; i < 1000; i++) {
			measureGetItemTime(100); // warmup
		}
		double elapsed_10 = measureGetItemTime(10);
		double elapsed_100000 = measureGetItemTime(100000);
		double ratio = elapsed_100000 / elapsed_10;

		System.out.printf("Time for 10 elements: %f ns\nTime for 100000 elements: %f ns\nRatio: %f\n",elapsed_10, elapsed_100000, ratio);
	}

	private long measureGetItemTime(int count) {
		Tree tree = createTree();
		try {
			TreeItem subject = new TreeItem(tree, SWT.NONE);

			createChildren(subject, count);
			readAndDispatch();

			// warmup
			for (int i = 0; i < 1000; i++) {
				System.nanoTime();
				subject.getItem(count - 1);
			}

			long start = System.nanoTime();
			for (int i = 0; i < 10000; i++) {
				subject.getItem(count - 1);
			}
			long stop = System.nanoTime();
			long elapsed = stop-start;
			return elapsed;
		} finally {
			tree.dispose();
		}
	}

	private void createChildren(TreeItem subject, int count) {
		for (int i = 0; i < count; i++) {
			new TreeItem(subject, SWT.NONE);
		}
	}

	private Tree createTree() {
		Tree result = new Tree(shell, SWT.NONE);
		return result;
	}

	private void readAndDispatch() {
		for (int i = 0; i < 10; i++ ) {
			while(shell.getDisplay().readAndDispatch()) {}
		}
	}


}

This test wrapped in a product:
testporduct.zip (sources)
slowGetItem.linux.gtk.x86_64.zip slowGetItem.linux.gtk.aarch64.zip (built product)

Expected behavior
Access time should not depend on the number of items.

Environment:

  1. Select the platform(s) on which the behavior is seen:
    • All OS
    • Windows
    • Linux
    • macOS
  1. Any GTK
  2. Any JRE

Version since
Was there always?

Workaround (or) Additional context
Initially reported in eclipse-platform/eclipse.platform.ui#649 Jface algorithms make navigation time proportional to a square of child count due to this problem.

Workaround: eclipse-platform/eclipse.platform.ui#810

GTK returns children count in O(N):

static gint
gtk_tree_store_iter_n_children (GtkTreeModel *tree_model,
				GtkTreeIter  *iter)
{
  GNode *node;
  gint i = 0;

  g_return_val_if_fail (iter == NULL || iter->user_data != NULL, 0);

  if (iter == NULL)
    node = G_NODE (GTK_TREE_STORE (tree_model)->priv->root)->children;
  else
    node = G_NODE (iter->user_data)->children;

  while (node)
    {
      i++;
      node = node->next;
    }

  return i;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions