QSortFilterProxyModel is super slow in Qt-5.15.2
-
Hi all,
I am currently using Qt-5.15.2 on MacOS 14.5 (MacBook Pro M1) and creating a feature that uses QSortFilterProxyModel. This feature is part of an existing complex application and upgrading to Qt 6 is not a choice at the moment. I noticed that whenever I tried to filter items, the process was extremely slow.
Here is my test case. I used customsortFilterModel example in Qt and simplified it.
- The original model is a simple model inherited from QAbstractItemModel.
- In this model, the rowCount is 25000 while columnCount is only 3.
- Removed the proxy view (QTreeView) from the example as it was very slow to even launch the app.
- Connected a button to do the filtering on proxyModel (QSortFilterProxyModel). In the slot of the button click, I called invalidateFilter() on the proxyModel.
- Override filterAcceptRow function to return true/false for even/odd rows respectively.
SourceModel::SourceModel(QObject* p_Parent) : QAbstractItemModel(p_Parent) { } SourceModel::~SourceModel() { } QVariant SourceModel::headerData(int p_Section, Qt::Orientation p_Orientation, int p_Role) const { if (p_Role != Qt::DisplayRole) return QVariant(); switch (p_Section) { case 1: return QVariant("Column 1"); case 2: return QVariant("Column 2"); default: return QVariant("Column 3"); } } QVariant SourceModel::data(const QModelIndex& p_Index, int p_Role) const { if (p_Role != Qt::DisplayRole) return QVariant(); switch (p_Index.column()) { case 1: return QVariant(p_Index.row() + 1); case 2: return QVariant(QString("Column 2 Row %1").arg(p_Index.row() + 1)); default: return QVariant(QString("Last Column Row %1").arg(p_Index.row() + 1)); } } int SourceModel::rowCount(const QModelIndex& p_Parent) const { return 25000; } int SourceModel::columnCount(const QModelIndex& p_Parent) const { return 3; } QModelIndex SourceModel::index(int p_Row, int p_Column, const QModelIndex& p_Parent) const { return createIndex(p_Row, p_Column); }
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { } void MySortFilterProxyModel::SetIsFilterRows(bool p_IsFilter) { isFilter = p_IsFilter; invalidateRowsFilter(); // invalidateFilter(); } bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { if (!isFilter) return true; return (sourceRow & 1); }
Window::Window() { proxyModel = new MySortFilterProxyModel(this); sourceView = new QTreeView; .... // Other UI inits QPushButton* btn = new QPushButton("Filter Items", this); btn->setCheckable(true); connect(btn, &QPushButton::toggled, proxyModel, &MySortFilterProxyModel::SetIsFilterRows); ... // Add to layouts } void Window::setSourceModel(QAbstractItemModel *model) { proxyModel->setSourceModel(model); sourceView->setModel(model); } void Window::showEvent(QShowEvent* p_Event) { const QModelIndex& index = proxyModel->mapToSource(proxyModel->index(0, 0)); // To force the proxy model to create mapping since there is no proxy view now. QWidget::showEvent(p_Event); }
Observations - Whenever the model is filtering data, it's extremely slow. It takes about 5 seconds plus. It is not even because of the view since I removed the proxy view. I have attached screenshot from time profile
I backported the following fixes, and used invalidateRowsFilter() instead
https://github.com/qt/qtbase/commit/8455bfee76ed3f1bd3bba8bd3688a7afa94ae0bb
https://github.com/qt/qtbase/commit/b5f6a85d274It improved the performance from 5 seconds to 3 seconds plus. But I still find it slow. Finally, I tried it with Qt6. And that improved the performance tremendously (~200ms). However, I am not able to find any other commits that would contribute to this performance improvement. I am suspecting the replacement of QVector to QList, but I am not sure.
Could any expert here please advise on what contributes to the performance improvement in Qt6. Thanks in advance.
-
Hi,
I see a lot of call to detach which seems odd for just showing data.
Can you share your model implementation ? -
I was able to fix this with the following:
https://github.com/qt/qtbase/commit/7d92ef63d7cAnd the following patch in Qt-5.15.2 which tremendously improved the timing:
-