GTK 2/3 是否支持高效的虚拟表?

Does GTK 2/3 support efficient virtual tables?

我们正在使用 SWT 开发 Java 应用程序,它提供 virtual tables。允许在 100 和 10M 之间切换 table 行数的测试代码在 Windows 和 OSX 上工作不到 100 毫秒,但在 Ubuntu 16.04 上需要几秒钟。现在我们想知道这是 SWT 的 GTK 实现的缺点还是 GTK 本身的缺点?在后一种情况下,GTK 2 和 GTK 3 之间有区别吗?

TLDR: 不幸的是,GTK 的 GtkTreeView 似乎根本不支持虚拟模式,而且它的实现方式对于大量项目。 SWT 选择使用 GtkTreeView 来实现 Table 并引入了更多的性能问题。

SWT Table.setItemCount()

Table.setItemCount() 的来源:

  • 存储库:http://git.eclipse.org/gitroot/platform/eclipse.platform.swt.git
  • 源文件:eclipse.platform.swt\bundles\org.eclipse.swt\Eclipse SWT\gtk\org\eclipse\swt\widgets\Table.java
  • 查找:public void setItemCount (int count) {

你会看到,即使在 VIRTUAL 模式下,setItemCount 也会分配所有项目,一次一个,使用 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

OS.gtk_list_store_append 的来源:

  • 存储库:http://git.eclipse.org/gitroot/platform/eclipse.platform.swt.git
  • 源文件:eclipse.platform.swt\bundles\org.eclipse.swt\Eclipse SWT\gtk\org\eclipse\swt\widgets\Table.java
  • 查找:public static final void gtk_list_store_append(long /*int*/ list_store, long /*int*/ iter) {

在这里,对于每个项目,都会获取和释放锁,并调用本地方法。我想他们至少应该通过一次本机调用分配所有项目。

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

  • 存储库:git://git.gnome.org/gtk+
  • 源文件:gtk/gtkliststore.c
  • 查找:gtk_list_store_append (GtkListStore *list_store,

它只是调用 gtk_list_store_insert(list_store, iter, -1)

GTKgtk_list_store_insert

  • 存储库:git://git.gnome.org/gtk+
  • 源文件:gtk/gtkliststore.c
  • 查找:gtk_list_store_insert (GtkListStore *list_store,

关键操作是 g_sequence_insert_before 插入 gsequence,这是一棵平衡树。

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);
}

不幸的是,此函数做了 很多 批量插入过程中不需要的事情,例如它每次都计算序列的长度(具有非平凡的时间复杂度) ,将索引转换为迭代器,触发树的更新通知等。

我进行了搜索,但没有找到任何适合批量插入的内容。

Windows实施

Windows Table 的实现使用 OS 提供的 ListView 控制,它在虚拟模式下有效实现(参见 LVS_OWNERDATA

另请参阅:Eclipse Bug 236863