Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is there no optimization for checking for empty string via "" comparison?

Tags:

c++

gcc

clang

c++14

This google-benchmark code checked on Quick Bench, shows that string::empty() runs a lot faster than comparing with an empty-string literal. However, creating a name string of "" actually makes the compiler optimize away the check:

bool compareWithNamedConst(const std::string& target) {
    const std::string emptyString = "";
    return emptyString == target;
}

bool compareWithLiteral(const std::string& target) {
    return "" == target;
}

bool compareWithRvalue(const std::string& target) {
    return std::string{""} == target;
}

bool checkForEmpty(const std::string& target) {
    return target.empty();
}

The performance for each of the calls is shown here:

As you can see, comparing with "" is very slow compared to all the other options. I wonder why it is the case? It must be somehow related to SSO not being applied on const char*, as testing this:

bool compareWithLiteral(const std::string& target) {
    return "test with a longer string not optimized" == target;
}

bool compareWithRvalue(const std::string& target) {
    return std::string{"test with a longer string not optimized"} == target;
}

Results comparing with a literal actually being faster:

I find that checking for string emptiness, the easiest syntax to read is "" == myVariable as it clearly indicates that myVariable is a std::string with no unneccesary clutter. Why can't we have it optimized as we have all the other cases?

like image 220
Adam Hunyadi Avatar asked Nov 06 '22 07:11

Adam Hunyadi


1 Answers

For emptyness check, if you really like the literal syntax, you may help your compiler by using namespace std::string_literals;, and replacing "" with ""s. In that case compareWithLiteral, compareWithRvalue and checkForEmpty would be essentially the same, since the operator== between const std::string& usually checks the sizes before the content, and the size of ""s is trivial.

bool compareWithLiteral(const std::string& target) {
    using namespace std::string_literals;
    return ""s == target;
}

For strings not falling into the SSO, you should try with std::string_view and its operator""sv, even if unfortunately is available only since C++17. Boost has boost::string_view. No user string literal is provided for compile time creation (essential because the test string length would be hardcoded in the binary), but you can easily define one:

#include <boost/utility/string_view.hpp>

constexpr auto operator""_sv(const char* str, std::size_t len) noexcept {
    return boost::basic_string_view<char>{str, len};
}

bool compareWithLiteral(const std::string& target) {
    return "test with a longer string not optimized"_sv == target;
}

This version runs much faster than your compareWithLiteral version, at least when target size is different.

like image 161
Giovanni Cerretani Avatar answered Nov 14 '22 23:11

Giovanni Cerretani