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

Model/View DataChanged performance



  • I have converted a widget that I wrote that was using QTableWidget by separating its code between 2 classes derived from QTableView and QAbstractTableModel.

    On first execution, I have found out that the performance of the new QTableView/QAbstractTableModel was horrible compared to what I had with the original QTableWidget code. We are talking almost 30% CPU usage vs sub 10% with prior version.

    I did dig a little bit and I found the reasons.

    1. The QVector<int> role parameters is pretty much ignored by the framework. So if in my code, I only change Qt::BackgroundRole, pretty much everything gets fetched again on each redraw. So if formatting for Qt::DisplayRole is expensive, you better cache the result.
    2. Based on the code that I saw in 5.13 (QAbstractItemView::DataChanged), the whole viewport is redrawn even if a single row has changed.

    To get back the original performance, I did the following workaround but it feels pretty much dumb that the framework is forcing me to do that...

    /*
     * Due to corners cut round in the framework,
     * (Check function QAbstractItemView::dataChanged() in qabstractitemview.cpp for the gory details
     *  - last time checked was in Qt 5.13. I would think that this will get resolved at some point...)
     * It is more efficient to report changes one index at a time than reporting a range in one shot.
     */
    
    void TradeBookModel::rowChanged(const QVector<int> &roles, int rowIdx, int firstCol, int lastCol) // internal
    {
    /*
        QModelIndex first = index(rowIdx, firstCol);
        QModelIndex last  = index(rowIdx, lastCol);
        emit dataChanged(first, last, roles);
    */
        while (firstCol <= lastCol) {
            QModelIndex curIdx = index(rowIdx, firstCol++);
            emit dataChanged(curIdx, curIdx, roles);
        }
    
    }
    
    void TradeBookModel::colChanged(const QVector<int> &roles, int colIdx, int firstRow, int lastRow) // internal
    {
    /*
        QModelIndex first = index(firstRow, colIdx);
        QModelIndex last  = index(lastRow, colIdx);
        emit dataChanged(first, last, roles);
    */
        while (firstRow <= lastRow) {
            QModelIndex curIdx = index(firstRow++, colIdx);
            emit dataChanged(curIdx, curIdx, roles);
        }
    }
    

    I thought that some people in the forum would find this discovery interesting... Beside this first encountered glitch... I'm otherwise having a good time using Qt... I have been using the framework for less than a month...

    Good job guys!


  • Lifetime Qt Champion

    @lano1106 said in Model/View DataChanged performance:

    I only change Qt::BackgroundRole, pretty much everything gets fetched again

    It's not about caching, it's about knowing if you need to redraw which is the case when the background changes.

    the whole viewport is redrawn even if a single row has changed.

    This is currently true, yes - the update() call should be fixed that only the changes regions are redrawn. But it has some drawbacks (e.g. when the size changes, or when they are a lot fo regions so the creaton of the region takes very long).


  • Lifetime Qt Champion

    @lano1106: I did some work on QAIV::dataChanged(): https://codereview.qt-project.org/c/qt/qtbase/+/285280


Log in to reply