QHash: optimize equality operator

- First compare the d-pointer before dipping into *d
- Keep a running count as we calculate thisEqualRange, as
  std::distance() on QHash::iterator is very expensive.
- Skip the pointless first comparison of the unadvanced
  iterator's key() with itself (found by Mårten Nordheim)

Also rename (it, thisEqualRangeEnd) → (thisEqualRangeStart, it),
to keep advancing `it`, which is more natural than advancing
an `end` and later resetting it = end.

Change-Id: I2c27c071b9ee23425a763328402dad9efee4cbd0
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Marc Mutz 2019-07-10 19:40:06 +02:00
parent 2d2e16d3ef
commit 0fec7417ca

View File

@ -957,23 +957,27 @@ Q_OUTOFLINE_TEMPLATE typename QHash<Key, T>::Node **QHash<Key, T>::findNode(cons
template <class Key, class T>
Q_OUTOFLINE_TEMPLATE bool QHash<Key, T>::operator==(const QHash &other) const
{
if (size() != other.size())
return false;
if (d == other.d)
return true;
if (size() != other.size())
return false;
const_iterator it = begin();
while (it != end()) {
// Build two equal ranges for i.key(); one for *this and one for other.
// For *this we can avoid a lookup via equal_range, as we know the beginning of the range.
auto thisEqualRangeEnd = it;
while (thisEqualRangeEnd != end() && it.key() == thisEqualRangeEnd.key())
++thisEqualRangeEnd;
auto thisEqualRangeStart = it;
const Key &thisEqualRangeKey = it.key();
size_type n = 0;
do {
++it;
++n;
} while (it != end() && it.key() == thisEqualRangeKey);
const auto otherEqualRange = other.equal_range(it.key());
const auto otherEqualRange = other.equal_range(thisEqualRangeKey);
if (std::distance(it, thisEqualRangeEnd) != std::distance(otherEqualRange.first, otherEqualRange.second))
if (n != std::distance(otherEqualRange.first, otherEqualRange.second))
return false;
// Keys in the ranges are equal by construction; this checks only the values.
@ -986,15 +990,13 @@ Q_OUTOFLINE_TEMPLATE bool QHash<Key, T>::operator==(const QHash &other) const
// is supported since MSVC 2015).
//
// ### Qt 6: if C++14 library support is a mandated minimum, remove the ifdef for MSVC.
if (!std::is_permutation(it, thisEqualRangeEnd, otherEqualRange.first
if (!std::is_permutation(thisEqualRangeStart, it, otherEqualRange.first
#ifdef Q_CC_MSVC
, otherEqualRange.second
#endif
)) {
return false;
}
it = thisEqualRangeEnd;
}
return true;