Почему нет оптимизации для проверки пустой строки с помощью сравнения ""? - PullRequest
5 голосов
/ 29 мая 2020

Этот код теста Google, проверенный на Quick Bench , показывает, что string::empty() работает намного быстрее, чем сравнение с литералом с пустой строкой. Однако создание строки имени "" фактически заставляет компилятор оптимизировать проверку:

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();
}

Здесь показана производительность для каждого из вызовов:

image

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;
}

Результаты сравнения с буквальным значением быстрее:

Я считаю, что при проверке пустоты строки проще всего читать синтаксис "" == myVariable, поскольку он четко указывает, что myVariable - это std::string без лишнего беспорядка. Почему мы не можем оптимизировать его, как все остальные случаи?

Ответы [ 2 ]

1 голос
/ 29 мая 2020

Для проверки пустоты, если вам действительно нравится буквальный синтаксис, вы можете помочь своему компилятору, using namespace std::string_literals; и заменив "" на ""s. В этом случае compareWithLiteral, compareWithRvalue и checkForEmpty будут по существу одинаковыми, поскольку operator== между const std::string& обычно проверяет размеры до содержимого, а размер ""s является тривиальным.

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

Для строк, не попадающих в SSO, вы должны попробовать с std::string_view и его operator""sv, даже если, к сожалению, он доступен только с C ++ 17. Boost имеет boost::string_view. Строковый литерал пользователя не предоставляется для создания во время компиляции (важно, поскольку длина тестовой строки будет жестко закодирована в двоичном коде), но вы можете легко определить его:

#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;
}

Эта версия работает намного быстрее, чем ваша compareWithLiteral версия, по крайней мере, если target размер отличается.

0 голосов
/ 29 мая 2020

Я считаю, что основное различие состоит в том, что при сравнении std::string объекты сначала только проверяются их размеры . Если они не равны, сравнение немедленно возвращает . (Это также может объяснить, почему тогда memcmp может использоваться, если обе строки имеют одинаковую длину, как указано @KamilCuk.)

Напротив, с литералами они обрабатываются как C - строки / массивы символов, длина которых не указана. Следовательно, внутри используется функция strcmp, которая имеет большие накладные расходы , даже если одна из строк пуста (по крайней мере, накладные расходы на вызов функции, поскольку strcmp кажется не встроенным в ваша сборка).

Что касается последнего вопроса, compareWithRvalue, там строка слишком велика для SSO, поэтому выполняется Dynami c выделение памяти (и освобождение) ( и, вероятно, здесь не оптимизирован), что представляет собой относительно большие накладные расходы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...