Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-130309

QSortFilterProxyModel invalidate is slow when there's a large number of persistent indicies

    XMLWordPrintable

Details

    • All
    • 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:

      1. QSortFilterProxyModel::invalidate() emits layoutAboutToBeChanged with no arguments.
      2. Selection model slot (QItemSelectionModelPrivate::_q_layoutAboutToBeChanged) requests all selected indicies to be saved as persistent.
      3. QSortFilterProxyModelPrivate::_q_clearMapping() tries to save and restore all persistent indicies in store_persistent_indexes() and update_persistent_indexes(...) respectively.
      4. 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&amp;gt;MainWindow</class&amp;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

          For Gerrit Dashboard: QTBUG-130309
          # Subject Branch Project Status CR V

          Activity

            People

              manordheim MÃ¥rten Nordheim
              nerodsm nerodsm
              Votes:
              0 Vote for this issue
              Watchers:
              7 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes