Details
-
Bug
-
Resolution: Fixed
-
P1: Critical
-
6.5.2
-
None
-
-
1558811a8 (dev), 19899d88e (6.8), 04e04005d (tqtc/lts-6.5)
Description
In short, my take is: changes to QHash in Qt6 made certain operations (a lot) less efficient.
Prerequisites: a model with a large number of indicies + proxy + selection model. Select a substantial number of indicies (at least a few thousands) -> invalidate proxy model -> process is hanging for n seconds (the larger the selection, the longer it takes to finish processing).
What I managed to figure out:
- QSortFilterProxyModel::invalidate() emits layoutAboutToBeChanged with no arguments.
- Selection model slot (QItemSelectionModelPrivate::_q_layoutAboutToBeChanged) requests all selected indicies to be saved as persistent.
- QSortFilterProxyModelPrivate::_q_clearMapping() tries to save and restore all persistent indicies in store_persistent_indexes() and update_persistent_indexes(...) respectively.
- QAbstractItemModel::changePersistentIndexList(..) is erasing and reinserting a large number of values one by one, which seems to be exetremely slow compared to Qt5 implementation.
MRE:
A table with 200x200 model and row selection.
Select some 10-20 rows and click the button.
mainwindow.ui
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class&gt;MainWindow</class&gt; <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>600</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralwidget"> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QTableView" name="tableView"> <property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</enum> </property> </widget> </item> <item> <widget class="QPushButton" name="pushButton"> <property name="text"> <string>Invalidate</string> </property> </widget> </item> </layout> </widget> </widget> <resources/> <connections/> </ui>
testtable.h
#ifndef TESTTABLE_H #define TESTTABLE_H #include <QAbstractTableModel> class TestTable : public QAbstractTableModel { Q_OBJECT public: explicit TestTable(QObject *parent = nullptr); // Header: QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; // Basic functionality: int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; private: }; #endif // TESTTABLE_H
testtable.cpp
#include "testtable.h" TestTable::TestTable(QObject *parent) : QAbstractTableModel(parent) { } QVariant TestTable::headerData(int section, Qt::Orientation orientation, int role) const { return QString::number(section); } int TestTable::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return 200; } int TestTable::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return 200; } QVariant TestTable::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); return QString("%1 - %2").arg(index.row()).arg(index.column()); }
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class TestTable; class QSortFilterProxyModel; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; TestTable *model; QSortFilterProxyModel *proxy; }; #endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include "testtable.h" #include <QSortFilterProxyModel> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , model(new TestTable) , proxy(new QSortFilterProxyModel) { ui->setupUi(this); proxy->setSourceModel(model); ui->tableView->setModel(proxy); QObject::connect(ui->pushButton, &QPushButton::clicked, proxy, &QSortFilterProxyModel::invalidate); } MainWindow::~MainWindow() { delete ui; }
main.cpp
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
Attachments
Issue Links
- duplicates
-
QTBUG-113613 [REG: 5->6] QMultiHash erase performance degraded from milliseconds to minutes
-
- Closed
-
For Gerrit Dashboard: QTBUG-130309 | ||||||
---|---|---|---|---|---|---|
# | Subject | Branch | Project | Status | CR | V |
599024,7 | Wrap QModelIndex in QSortFilterProxyModel for hash performance | dev | qt/qtbase | Status: MERGED | +2 | 0 |
600625,2 | Wrap QModelIndex in QSortFilterProxyModel for hash performance | 6.8 | qt/qtbase | Status: MERGED | +2 | 0 |
600689,3 | Wrap QModelIndex in QSortFilterProxyModel for hash performance | tqtc/lts-6.5 | qt/tqtc-qtbase | Status: MERGED | +2 | 0 |