Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QStandardItemModel+QTableView get pool performance after 5.9.3



  • Hi,
    I found that my app got pool performance after using qt5.9.4 and above.
    My App uses QStandardItemModel+QTableView to show about 1M rows and each has 5 columns.
    When using 5.9.3, I try add rows as quick as possilbe(about 20k rows/s),and the UI is responsible. CPU is not 100% yet.
    But from 5.9.4, (I have tried many versions including 5.11.x 5.12.x 5.13.x, in fact), GUI stuck quickly even when I add rows below 2k/s. CPU usage reach 100% for a long time.

    Does anyone know something about this? I know my problem is not clear, but just want to try my luck.



  • After some research myself, I think I got the root cause.
    Below is linux perf profile result
    Screenshot_20191020_123137.png
    Difference begins from QHeaderViewPrivate::_q_layoutChanged().
    Qt5.9.3 doesn't call QHeaderView::initializeSections(), but 5.9.4 and later versions call it, which cost a lot of CPU.
    Here is the change
    Screenshot_20191020_123721.png


  • Lifetime Qt Champion

    If you want performance, don't use convenience models like QStandardItemModel. This stuff is needed to make sure the hidden sections stay the same after an insert (which wasn't the case previously).
    But you can try to create a small example so we can reproduce your problem.



  • @Christian-Ehrlicher
    OK. Here is the example. When I compile it with qt593, I can drag the scrollbar anytime. Higher version of Qt will stuck quickly.

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QThread>
    class QStandardItemModel;
    class AA : public QThread
    {
        Q_OBJECT
    signals:
        void add();
    protected:
        void run(){
            sleep(5);
            for(int i=0;i<1000000;i++){
                emit add();
                QThread::usleep(100);
            }
        }
    };
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = 0);
        ~MainWindow();
    private:
        QStandardItemModel *model;
    };
    
    #endif // MAINWINDOW_H
    
    #include "mainwindow.h"
    #include <QTableView>
    #include <QStandardItemModel>
    #include <QThread>
    #include <QTimer>
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
    {
        model = new QStandardItemModel(0, 5);
        int i = 0;
        model->setHorizontalHeaderItem(i++, new QStandardItem(QObject::tr("Col1")));
        model->setHorizontalHeaderItem(i++, new QStandardItem(QObject::tr("Col2")));
        model->setHorizontalHeaderItem(i++, new QStandardItem(QObject::tr("Col3")));
        model->setHorizontalHeaderItem(i++, new QStandardItem(QObject::tr("Col4")));
        model->setHorizontalHeaderItem(i++, new QStandardItem(QObject::tr("Col5")));
        QTableView *view = new QTableView;
        setCentralWidget(view);
        view->setModel(model);
    
        AA *aa = new AA;
        connect(aa, &AA::add, this, [=]{
            int row = model->rowCount();
            int i = 0;
            model->setItem(row, i++, new QStandardItem("AAA"));
            model->setItem(row, i++, new QStandardItem("BBB"));
            model->setItem(row, i++, new QStandardItem("CCC"));
            model->setItem(row, i++, new QStandardItem("DDD"));
            model->setItem(row, i++, new QStandardItem("EEE"));
        });
        aa->start();
    }
    
    MainWindow::~MainWindow()
    {
    
    }
    

  • Lifetime Qt Champion

    Thx for the reproducer - but I strongly suggest to use a custom model instead QStandardItemModel - it's slow by design since for example every setItem() call triggers an update to the view which is way to much (and unneeded when a correct model is used)


  • Lifetime Qt Champion

    There were some changes in QHeaderViewPrivate::_q_sectionsChanged() (previously _q_layoutChanged) in 5.11. Maybe it helps you.
    I tested you stuff with a simple custom model and it really speeds up the stuff. Remove QThread::usleep() and got the following times:
    Custom Model:

    real    0m5,352s
    user    0m0,373s
    sys     0m0,091s
    

    QStandardItemModel:

    real    0m59,377s
    user    0m54,159s
    sys     0m0,298s
    

    Dummy testcode

    int TableModel::rowCount(const QModelIndex &parent) const
    {
        return parent.isValid() ? 0 : m_rowCount;
    }
    
    int TableModel::columnCount(const QModelIndex &parent) const
    {
        return parent.isValid() ? 0 : 5;
    }
    
    QVariant TableModel::data(const QModelIndex &index, int role) const
    {
        if (role != Qt::DisplayRole || !index.isValid())
            return QVariant();
        switch (index.column())
        {
        case 0: return QStringLiteral("AAA");
        case 1: return QStringLiteral("BBB");
        case 2: return QStringLiteral("CCC");
        case 3: return QStringLiteral("DDD");
        case 4: return QStringLiteral("EEE");
        }
        return QVariant();
    }
    
    void TableModel::addRow()
    {
        beginInsertRows(QModelIndex(), m_rowCount, m_rowCount);
        ++m_rowCount;
        endInsertRows();
    }
    


  • @Christian-Ehrlicher
    OK. I will try to use custom model if this is not a bug.
    Thanks a lot!


  • Lifetime Qt Champion

    @Mr-Pang said in QStandardItemModel+QTableView get pool performance after 5.9.3:

    if this is not a bug.

    It's maybe a speed regression and maybe you should file a bug report about this (with your testcase) - maybe someone finds a better solution for the problems which introduced this regression.



  • I found the real problem finally. I use model->setItem() to append new row. The correct way is model->appendRow().
    QStandardItemModel works well now.


  • Lifetime Qt Champion

    See also https://bugreports.qt.io/browse/QTBUG-18539 for more information about this problem.


Log in to reply