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> template <class Key, class T>
Q_OUTOFLINE_TEMPLATE bool QHash<Key, T>::operator==(const QHash &other) const Q_OUTOFLINE_TEMPLATE bool QHash<Key, T>::operator==(const QHash &other) const
{ {
if (size() != other.size())
return false;
if (d == other.d) if (d == other.d)
return true; return true;
if (size() != other.size())
return false;
const_iterator it = begin(); const_iterator it = begin();
while (it != end()) { while (it != end()) {
// Build two equal ranges for i.key(); one for *this and one for other. // 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. // For *this we can avoid a lookup via equal_range, as we know the beginning of the range.
auto thisEqualRangeEnd = it; auto thisEqualRangeStart = it;
while (thisEqualRangeEnd != end() && it.key() == thisEqualRangeEnd.key()) const Key &thisEqualRangeKey = it.key();
++thisEqualRangeEnd; 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; return false;
// Keys in the ranges are equal by construction; this checks only the values. // 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). // is supported since MSVC 2015).
// //
// ### Qt 6: if C++14 library support is a mandated minimum, remove the ifdef for MSVC. // ### 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 #ifdef Q_CC_MSVC
, otherEqualRange.second , otherEqualRange.second
#endif #endif
)) { )) {
return false; return false;
} }
it = thisEqualRangeEnd;
} }
return true; return true;