From 5f6b81405737a9a056fc8259c5f032c2e1791583 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 17 Dec 2025 17:49:01 +0100 Subject: [PATCH] Improve performance of til::equals (#19653) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As explained in the added comment. Perf 📈. --- src/inc/til/string.h | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/inc/til/string.h b/src/inc/til/string.h index 824df3212b..2fb0328273 100644 --- a/src/inc/til/string.h +++ b/src/inc/til/string.h @@ -159,16 +159,29 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" // Just like std::wstring_view::operator==(). // - // At the time of writing wmemcmp() is not an intrinsic for MSVC, - // but the STL uses it to implement wide string comparisons. - // This produces 3x the assembly _per_ comparison and increases - // runtime by 2-3x for strings of medium length (16 characters) - // and 5x or more for long strings (128 characters or more). + // NOTE: If you pass a literal, ALWAYS pass it as the 2nd argument. + // This is because MSVC does not understand that `lhs.size() == rhs.size()` implies + // that a constant size on one side implies a constant size on the other side. + // As such the size argument to memcmp() must be the constant size, which is `rhs`. + // + // The STL uses wmemcmp() to implement wide string comparisons. + // Compared to memcmp() this results 3x the assembly per comparison, + // while running 2-3x slower for 16 chars and >5x slower for >128 chars. // See: https://github.com/microsoft/STL/issues/2289 template - bool equals(const std::basic_string_view& lhs, const std::basic_string_view& rhs) noexcept + constexpr bool equals(const std::basic_string_view& lhs, const std::basic_string_view& rhs) noexcept { - return lhs.size() == rhs.size() && __builtin_memcmp(lhs.data(), rhs.data(), lhs.size() * sizeof(T)) == 0; + return lhs.size() == rhs.size() && __builtin_memcmp(lhs.data(), rhs.data(), rhs.size() * sizeof(T)) == 0; + } + + constexpr bool equals(const std::string_view& lhs, const std::string_view& rhs) noexcept + { + return equals<>(lhs, rhs); + } + + constexpr bool equals(const std::wstring_view& lhs, const std::wstring_view& rhs) noexcept + { + return equals<>(lhs, rhs); } // Just like _memicmp, but without annoying locales.