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
我们正在使用 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