From 8a7b74f6e71d6b8264f365148bf5a04db4038617 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 23 Sep 2020 19:32:24 +0200 Subject: [PATCH] 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 --- src/corelib/text/qanystringview.h | 5 ++- src/corelib/text/qstring.cpp | 57 ++++++++++++++++++++++++++++ src/corelib/text/qstring.h | 11 ++---- src/corelib/text/qstringalgorithms.h | 10 +++++ src/corelib/text/qutf8stringview.h | 12 ++++-- 5 files changed, 82 insertions(+), 13 deletions(-) diff --git a/src/corelib/text/qanystringview.h b/src/corelib/text/qanystringview.h index 647f43f265..c073eda3b6 100644 --- a/src/corelib/text/qanystringview.h +++ b/src/corelib/text/qanystringview.h @@ -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 diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp index 2838e513f1..71e51ab0fc 100644 --- a/src/corelib/text/qstring.cpp +++ b/src/corelib/text/qstring.cpp @@ -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 lhs, QStringView rhs) noexcept +{ + return QUtf8::compareUtf8(lhs, rhs) == 0; +} + +bool QtPrivate::equalStrings(QStringView lhs, QBasicUtf8StringView rhs) noexcept +{ + return QtPrivate::equalStrings(rhs, lhs); +} + +bool QtPrivate::equalStrings(QLatin1String lhs, QBasicUtf8StringView rhs) noexcept +{ + QString r = rhs.toString(); + return QtPrivate::equalStrings(lhs, r); // ### optimize! +} + +bool QtPrivate::equalStrings(QBasicUtf8StringView lhs, QLatin1String rhs) noexcept +{ + return QtPrivate::equalStrings(rhs, lhs); +} + +bool QtPrivate::equalStrings(QBasicUtf8StringView lhs, QBasicUtf8StringView 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 diff --git a/src/corelib/text/qstring.h b/src/corelib/text/qstring.h index 5c809592e8..00efa8d1b0 100644 --- a/src/corelib/text/qstring.h +++ b/src/corelib/text/qstring.h @@ -1042,11 +1042,6 @@ QString QBasicUtf8StringView::toString() const return QString::fromUtf8(data(), int(size())); } -template -inline int QBasicUtf8StringView::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; } diff --git a/src/corelib/text/qstringalgorithms.h b/src/corelib/text/qstringalgorithms.h index 5e2eb1912a..6d8fe114f1 100644 --- a/src/corelib/text/qstringalgorithms.h +++ b/src/corelib/text/qstringalgorithms.h @@ -86,6 +86,16 @@ namespace QtPrivate { [[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION int compareStrings(QBasicUtf8StringView lhs, QLatin1String rhs, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept; [[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION int compareStrings(QBasicUtf8StringView lhs, QBasicUtf8StringView 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 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 rhs) noexcept; +[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool equalStrings(QBasicUtf8StringView lhs, QStringView rhs) noexcept; +[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool equalStrings(QBasicUtf8StringView lhs, QLatin1String rhs) noexcept; +[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool equalStrings(QBasicUtf8StringView lhs, QBasicUtf8StringView 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; diff --git a/src/corelib/text/qutf8stringview.h b/src/corelib/text/qutf8stringview.h index 98357ae5a9..55621588bd 100644 --- a/src/corelib/text/qutf8stringview.h +++ b/src/corelib/text/qutf8stringview.h @@ -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(lhs.data(), lhs.size()), + QBasicUtf8StringView(rhs.data(), rhs.size())); + } [[nodiscard]] friend inline bool operator==(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept - { return QBasicUtf8StringView::compare(lhs, rhs) == 0; } + { + return QtPrivate::equalStrings(QBasicUtf8StringView(lhs.data(), lhs.size()), + QBasicUtf8StringView(rhs.data(), rhs.size())); + } [[nodiscard]] friend inline bool operator!=(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept { return !operator==(lhs, rhs); }