Optimize equality operators for string classes

Using compare() for those operators is not a good idea, as we can't
shortcut comparisons if the string sizes are different. This alone
made our HTML parser in QtGui around 15% slower.

Don't go through QAnyStringView to implement compare() for
QUtf8StringView, use QtPrivate::compareStrings() directly instead.

Task-number: QTBUG-86354
Change-Id: I04869c29c9918161990dc1baf8e943b3a264ff3c
Reviewed-by: Karsten Heimrich <karsten.heimrich@qt.io>
This commit is contained in:
Lars Knoll 2020-09-23 19:32:24 +02:00
parent 3807559d37
commit 8a7b74f6e7
5 changed files with 82 additions and 13 deletions

View File

@ -200,6 +200,7 @@ public:
[[nodiscard]] constexpr const void *data() const noexcept { return m_data; }
[[nodiscard]] Q_CORE_EXPORT static int compare(QAnyStringView lhs, QAnyStringView rhs, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
[[nodiscard]] Q_CORE_EXPORT static bool equal(QAnyStringView lhs, QAnyStringView rhs) noexcept;
//
// STL compatibility API:
@ -224,9 +225,9 @@ public:
private:
[[nodiscard]] friend inline bool operator==(QAnyStringView lhs, QAnyStringView rhs) noexcept
{ return QAnyStringView::compare(lhs, rhs) == 0; }
{ return QAnyStringView::equal(lhs, rhs); }
[[nodiscard]] friend inline bool operator!=(QAnyStringView lhs, QAnyStringView rhs) noexcept
{ return !operator==(lhs, rhs); }
{ return !QAnyStringView::equal(lhs, rhs); }
#ifdef __cpp_impl_three_way_comparison
[[nodiscard]] friend inline auto operator<=>(QAnyStringView lhs, QAnyStringView rhs) noexcept

View File

@ -1325,6 +1325,63 @@ int QAnyStringView::compare(QAnyStringView lhs, QAnyStringView rhs, Qt::CaseSens
});
}
bool QtPrivate::equalStrings(QStringView lhs, QStringView rhs) noexcept
{
return ucstrcmp(lhs.begin(), lhs.size(), rhs.begin(), rhs.size()) == 0;
}
bool QtPrivate::equalStrings(QStringView lhs, QLatin1String rhs) noexcept
{
return ucstrcmp(lhs.begin(), lhs.size(), rhs.begin(), rhs.size()) == 0;
}
bool QtPrivate::equalStrings(QLatin1String lhs, QStringView rhs) noexcept
{
return QtPrivate::equalStrings(rhs, lhs);
}
bool QtPrivate::equalStrings(QLatin1String lhs, QLatin1String rhs) noexcept
{
return lhs.size() == rhs.size() && (!lhs.size() || qstrncmp(lhs.data(), rhs.data(), lhs.size()) == 0);
}
bool QtPrivate::equalStrings(QBasicUtf8StringView<false> lhs, QStringView rhs) noexcept
{
return QUtf8::compareUtf8(lhs, rhs) == 0;
}
bool QtPrivate::equalStrings(QStringView lhs, QBasicUtf8StringView<false> rhs) noexcept
{
return QtPrivate::equalStrings(rhs, lhs);
}
bool QtPrivate::equalStrings(QLatin1String lhs, QBasicUtf8StringView<false> rhs) noexcept
{
QString r = rhs.toString();
return QtPrivate::equalStrings(lhs, r); // ### optimize!
}
bool QtPrivate::equalStrings(QBasicUtf8StringView<false> lhs, QLatin1String rhs) noexcept
{
return QtPrivate::equalStrings(rhs, lhs);
}
bool QtPrivate::equalStrings(QBasicUtf8StringView<false> lhs, QBasicUtf8StringView<false> rhs) noexcept
{
return lhs.size() == rhs.size() && (!lhs.size() || qstrncmp(lhs.data(), rhs.data(), lhs.size()) == 0);
}
bool QAnyStringView::equal(QAnyStringView lhs, QAnyStringView rhs) noexcept
{
if (lhs.size() != rhs.size() && lhs.isUtf8() == rhs.isUtf8())
return false;
return lhs.visit([rhs](auto lhs) {
return rhs.visit([lhs](auto rhs) {
return QtPrivate::equalStrings(lhs, rhs);
});
});
}
/*!
\relates QStringView
\internal

View File

@ -1042,11 +1042,6 @@ QString QBasicUtf8StringView<UseChar8T>::toString() const
return QString::fromUtf8(data(), int(size()));
}
template<bool UseChar8T>
inline int QBasicUtf8StringView<UseChar8T>::compare(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs,
Qt::CaseSensitivity cs) noexcept
{ return QAnyStringView::compare(lhs, rhs, cs); }
//
// QAnyStringView inline members that require QString:
//
@ -1444,7 +1439,7 @@ inline bool operator<=(QLatin1String lhs, QChar rhs) noexcept { return !(rhs <
inline bool operator>=(QLatin1String lhs, QChar rhs) noexcept { return !(rhs > lhs); }
// QStringView <> QStringView
inline bool operator==(QStringView lhs, QStringView rhs) noexcept { return lhs.size() == rhs.size() && QtPrivate::compareStrings(lhs, rhs) == 0; }
inline bool operator==(QStringView lhs, QStringView rhs) noexcept { return lhs.size() == rhs.size() && QtPrivate::equalStrings(lhs, rhs); }
inline bool operator!=(QStringView lhs, QStringView rhs) noexcept { return !(lhs == rhs); }
inline bool operator< (QStringView lhs, QStringView rhs) noexcept { return QtPrivate::compareStrings(lhs, rhs) < 0; }
inline bool operator<=(QStringView lhs, QStringView rhs) noexcept { return QtPrivate::compareStrings(lhs, rhs) <= 0; }
@ -1467,14 +1462,14 @@ inline bool operator> (QChar lhs, QStringView rhs) noexcept { return QStringView
inline bool operator>=(QChar lhs, QStringView rhs) noexcept { return QStringView(&lhs, 1) >= rhs; }
// QStringView <> QLatin1String
inline bool operator==(QStringView lhs, QLatin1String rhs) noexcept { return lhs.size() == rhs.size() && QtPrivate::compareStrings(lhs, rhs) == 0; }
inline bool operator==(QStringView lhs, QLatin1String rhs) noexcept { return lhs.size() == rhs.size() && QtPrivate::equalStrings(lhs, rhs); }
inline bool operator!=(QStringView lhs, QLatin1String rhs) noexcept { return !(lhs == rhs); }
inline bool operator< (QStringView lhs, QLatin1String rhs) noexcept { return QtPrivate::compareStrings(lhs, rhs) < 0; }
inline bool operator<=(QStringView lhs, QLatin1String rhs) noexcept { return QtPrivate::compareStrings(lhs, rhs) <= 0; }
inline bool operator> (QStringView lhs, QLatin1String rhs) noexcept { return QtPrivate::compareStrings(lhs, rhs) > 0; }
inline bool operator>=(QStringView lhs, QLatin1String rhs) noexcept { return QtPrivate::compareStrings(lhs, rhs) >= 0; }
inline bool operator==(QLatin1String lhs, QStringView rhs) noexcept { return lhs.size() == rhs.size() && QtPrivate::compareStrings(lhs, rhs) == 0; }
inline bool operator==(QLatin1String lhs, QStringView rhs) noexcept { return lhs.size() == rhs.size() && QtPrivate::equalStrings(lhs, rhs); }
inline bool operator!=(QLatin1String lhs, QStringView rhs) noexcept { return !(lhs == rhs); }
inline bool operator< (QLatin1String lhs, QStringView rhs) noexcept { return QtPrivate::compareStrings(lhs, rhs) < 0; }
inline bool operator<=(QLatin1String lhs, QStringView rhs) noexcept { return QtPrivate::compareStrings(lhs, rhs) <= 0; }

View File

@ -86,6 +86,16 @@ namespace QtPrivate {
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION int compareStrings(QBasicUtf8StringView<false> lhs, QLatin1String rhs, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION int compareStrings(QBasicUtf8StringView<false> lhs, QBasicUtf8StringView<false> rhs, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool equalStrings(QStringView lhs, QStringView rhs) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool equalStrings(QStringView lhs, QLatin1String rhs) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool equalStrings(QStringView lhs, QBasicUtf8StringView<false> rhs) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool equalStrings(QLatin1String lhs, QStringView rhs) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool equalStrings(QLatin1String lhs, QLatin1String rhs) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool equalStrings(QLatin1String lhs, QBasicUtf8StringView<false> rhs) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool equalStrings(QBasicUtf8StringView<false> lhs, QStringView rhs) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool equalStrings(QBasicUtf8StringView<false> lhs, QLatin1String rhs) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool equalStrings(QBasicUtf8StringView<false> lhs, QBasicUtf8StringView<false> rhs) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool startsWith(QStringView haystack, QStringView needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool startsWith(QStringView haystack, QLatin1String needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool startsWith(QLatin1String haystack, QStringView needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;

View File

@ -306,11 +306,17 @@ public:
#endif
private:
[[nodiscard]] static inline int compare(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs,
Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
[[nodiscard]] static inline int compare(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept
{
return QtPrivate::compareStrings(QBasicUtf8StringView<false>(lhs.data(), lhs.size()),
QBasicUtf8StringView<false>(rhs.data(), rhs.size()));
}
[[nodiscard]] friend inline bool operator==(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept
{ return QBasicUtf8StringView::compare(lhs, rhs) == 0; }
{
return QtPrivate::equalStrings(QBasicUtf8StringView<false>(lhs.data(), lhs.size()),
QBasicUtf8StringView<false>(rhs.data(), rhs.size()));
}
[[nodiscard]] friend inline bool operator!=(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept
{ return !operator==(lhs, rhs); }