如何提升 QAbstractItemModel::match 的效能
針對常用的match role, 可利用QHash (or QMap) 多存一份, 來加快 search 效率我們在設計model通常都會定義一個存unique string value的IdRole當作model item的索引, 且會很頻繁對這個role作match, 從"flags對QAbstractItemModel::match的效能影響"中的實驗中可知,
針對string value去作match在效率上是很糟的, 所以只要model數量大到一個程度, 就會很容易使UI thread hang住. 針對這部分的參考解法大致如下,
- maintain一個QHash把每個model item的IdRole value跟相對應的model index存起來
- 複寫match, 如果是是要查IdRole, 就直接從QHash找以提升效率
- 由於QStandardItemModel::clear本身不會觸發rowsAboutToBeRemoved, 所以即使呼叫clear, 還是要主動去清掉hash
#include <QHash>
#include <QModelIndex>
#include <QPersistentModelIndex>
#include <QStandardItemModel>
#include <QVariant>
class MyListModel : public QStandardItemModel
{
Q_OBJECT
public:
enum Roles
{
IdRole = Qt::UserRole + 1,
DataRole
};
MyListModel(QObject *parent = nullptr)
{
// 把每個model item的IdRole value存到QHash裡, key是id, value就是model index (必須用QPersistentModelIndex)
// 如果是tree model記得要recursive把childrn也加進來,
// 主要是如果使用者是先把model item的children都append好, 再append本身進來的話, 只會觸發一次rowsInserted.
QObject::connect(this, &QAbstractItemModel::rowsInserted, [=](const QModelIndex &parent, int start, int end) {
for (int row = start; row <= end; row++)
{
auto mdIndex = index(row, 0, parent);
auto id = data(mdIndex, MyListModel::IdRole);
m_idModelIndexHash.insert(id, QPersistentModelIndex(mdIndex));
}
});
QObject::connect(this, &QAbstractItemModel::rowsAboutToBeRemoved, this, [=](const QModelIndex &parent, int start, int end) {
for (int row = start; row <= end; row++)
{
auto mdIndex = index(row, 0, parent);
auto id = data(mdIndex, MyListModel::IdRole);
m_idModelIndexHash.remove(id);
}
});
}
virtual ~MyListModel() = default;
// 覆寫match function, 如果針對IdRole就去QHash以提升效率
virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) const override
{
if (role != MyListModel::IdRole)
{
return QStandardItemModel::match(start, role, value, hits, flags);
}
// todo, !m_idModelIndexHash.contains just return empty can enhance performance, but m_idModelIndexHash must be 100% correct
if (!m_idModelIndexHash.contains(value))
{
return QModelIndexList();
}
return QModelIndexList() << m_idModelIndexHash.value(value);
}
// 由於QStandardItemModel::clear本身不會觸發rowsAboutToBeRemoved, 所以clear記得要主動清掉hash
void clearIdHash()
{
m_idModelIndexMap.clear();
}
QHash<int, QByteArray> roleNames() const override
{
QHash<int, QByteArray> hash;
hash[MyListModel::DataRole] = "dataRole";
hash[MyListModel::IdRole] = "idRole";
return hash;
}
protected:
QHash<QVariant, QPersistentModelIndex> m_idModelIndexHash;
};
延續"flags對QAbstractItemModel::match的效能影響"中的實驗方法,
如果利用QHash得到的結果如下,
match from | elapsed (ms) |
---|---|
QStandardItemModel::match | 55194 |
QHash | 63 |
0 comments