qtqabstractitemmodelqmodelindex

Destructor of QModelIndexList is too slow


The execution of this simple snippet:

{
  QModelIndexList sel = ui->tableView->selectionModel()->selectedRows(0);
  sel.at(0).isValid(); // To prevent removing the previous line by optimization
}

takes more than 30 seconds when the number of selected rows is about one million. The construction of QModelIndex list is almost immediate, but the destruction takes forever. The time is spent in this function :

template <typename T>
Q_INLINE_TEMPLATE void QList<T>::node_destruct(Node *from, Node *to)
{
    if (QTypeInfo<T>::isLarge || QTypeInfo<T>::isStatic)
        while(from != to) --to, delete reinterpret_cast<T*>(to->v);
    else if (QTypeInfo<T>::isComplex)
        while (from != to) --to, reinterpret_cast<T*>(to)->~T();
}

Does somebody has a solution? Is there any way to get indexes of selected rows without creating QModelIndexList, or can I speedup the destruction somehow?


Solution

  • A QList will, unfortunately, perform a memory allocation and deletion on every model index. For some reason, on your platform, the deallocation is very slow.

    As a workaround, you can run the deallocation in a worker thread, leveraging the fact that QList is an implicitly shared class.

    This assumes that it's safe to delete a QModelIndex in a non-gui thread. You must audit your code and relevant Qt code to ascertain that.

    C++11

    auto sel{ui->tableView->selectionModel()->selectedRows(0)};
    // Use sel.
    sel.at(0).isValid();
    // Deallocate in a separate thread.
    QtConcurrent::run(std::bind([] (QModelIndexList& p) {}, std::move(sel)));
    // At this point, sel has been moved from and its destruction is trivial.
    

    C++14

    auto sel{ui->tableView->selectionModel()->selectedRows(0)};
    // Use sel.
    sel.at(0).isValid();
    // Deallocate in a separate thread.
    QtConcurrent::run([sel{std::move(sel)}] {});
    // At this point, sel has been moved from and its destruction is trivial.
    

    See this question for techniques of lambda-capture of a movable type.

    C++98

    template <typename T> class CopyMoves {
       mutable T data;
    public:
       CopyMoves(T & old) { std::swap(data, old); }
       CopyMoves(const CopyMoves & old) { std::swap(data, old.data); }
       void operator()() {}
    };
    
    int main() {
       QModelIndexList sel;
       QtConcurrent::run(CopyMoves<QModelIndexList>(sel));
    }
    

    The CopyMoves class implements a class that moves its data member upon copy-construction. This is the horrible hack used by std::auto_ptr (don't use auto_ptr!). The non-empty CopyMoves::data member will be destructed in the worker thread. The other two instances of CopyMoves, holding empty data, will be destructed in the main thread.