1

We are developing a Java application using SWT which offers virtual tables. Test code which allows to switch the number of table rows between 100 and 10M works in less than 100ms on Windows and OSX, but takes several seconds on Ubuntu 16.04. Now we are wondering whether this is a shortcoming of SWT's GTK implementation or of GTK itself? In the latter case, is there a difference between GTK 2 and GTK 3?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
mstrap
  • 16,808
  • 10
  • 56
  • 86

1 Answers1

3

TLDR: Unfortunately, GTK's GtkTreeView doesn't seem to support virtual mode at all, and it's implemented in a way that is very inefficient for large number of items. SWT chose to implement Table using GtkTreeView and introduced further problems with performance.

SWT Table.setItemCount()

Source for Table.setItemCount():

  • Repository: http://git.eclipse.org/gitroot/platform/eclipse.platform.swt.git
  • Source file: eclipse.platform.swt\bundles\org.eclipse.swt\Eclipse SWT\gtk\org\eclipse\swt\widgets\Table.java
  • Find: public void setItemCount (int count) {

You will see that even in VIRTUAL mode, setItemCount will allocate all items, one at a time, using OS.gtk_list_store_append() :

for (int i=itemCount; i<count; i++) {
    OS.gtk_list_store_append (modelHandle, iter);
}

SWT OS.gtk_list_store_append

Source for OS.gtk_list_store_append :

  • Repository: http://git.eclipse.org/gitroot/platform/eclipse.platform.swt.git
  • Source file: eclipse.platform.swt\bundles\org.eclipse.swt\Eclipse SWT\gtk\org\eclipse\swt\widgets\Table.java
  • Find: public static final void gtk_list_store_append(long /*int*/ list_store, long /*int*/ iter) {

Here, for every item a lock is acquired and released and native method is called. I guess they should have allocated all items with a single native call, at least.

public static final void gtk_list_store_append(long /*int*/ list_store, long /*int*/ iter) {
    lock.lock();
    try {
        _gtk_list_store_append(list_store, iter);
    } finally {
        lock.unlock();
    }
}

GTK gtk_list_store_append

  • Repository: git://git.gnome.org/gtk+
  • Source file: gtk/gtkliststore.c
  • Find: gtk_list_store_append (GtkListStore *list_store,

It merely calls gtk_list_store_insert(list_store, iter, -1)

GTK gtk_list_store_insert

  • Repository: git://git.gnome.org/gtk+
  • Source file: gtk/gtkliststore.c
  • Find: gtk_list_store_insert (GtkListStore *list_store,

The key action is g_sequence_insert_before that inserts into a gsequence, which is a balanced tree.

void
gtk_list_store_insert (GtkListStore *list_store,
               GtkTreeIter  *iter,
               gint          position)
{
  GtkListStorePrivate *priv;
  GtkTreePath *path;
  GSequence *seq;
  GSequenceIter *ptr;
  gint length;

  g_return_if_fail (GTK_IS_LIST_STORE (list_store));
  g_return_if_fail (iter != NULL);

  priv = list_store->priv;

  priv->columns_dirty = TRUE;

  seq = priv->seq;

  length = g_sequence_get_length (seq);
  if (position > length || position < 0)
    position = length;

  ptr = g_sequence_get_iter_at_pos (seq, position);
  ptr = g_sequence_insert_before (ptr, NULL);

  iter->stamp = priv->stamp;
  iter->user_data = ptr;

  g_assert (iter_is_valid (iter, list_store));

  priv->length++;

  path = gtk_tree_path_new ();
  gtk_tree_path_append_index (path, position);
  gtk_tree_model_row_inserted (GTK_TREE_MODEL (list_store), path, iter);
  gtk_tree_path_free (path);
}

Unfortunately, this function does a lot of things that are not necessary during a bulk insert, for example it calculates the length of sequence (which has nontrivial time complexity) each time, converts indices to iterators, fires update notifications for the tree, etc etc.

I searched, but didn't find anything for bulk inserts.

Windows implementation

Windows implementation of Table uses OS-provided ListView control, which is efficiently implemented for virtual mode (see LVS_OWNERDATA)

See also: Eclipse Bug 236863

Codeguard
  • 7,787
  • 2
  • 38
  • 41