На реализацию std :: string перемещается - PullRequest
0 голосов
/ 29 мая 2018

Я исследовал производительность перемещения std::string.Долгое время я считал движения строк почти свободными, полагая, что компилятор встроит все, и для этого потребуется только несколько дешевых заданий.

На самом деле моя ментальная модель перемещения буквально

string& operator=(string&& rhs) noexcept
{
    swap(*this, rhs);
    return *this;
}

friend void swap(string& x, string& y) noexcept
{
    // for disposition only
    unsigned char buf[sizeof(string)];
    memcpy(buf, &x, sizeof(string));
    memcpy(&x, &y, sizeof(string));
    memcpy(&y, buf, sizeof(string));
}

Насколько я понимаю, это правовая реализация, если memcpy изменяется на присвоение отдельных полей.

К моему большому удивлению, я обнаружил, что gcc реализует перемещение, включающее создание новой строки и могут, возможно, выдать из-за распределений, несмотря на то, что noexcept.

Это даже соответствует?Не менее важно, разве я не думаю, что движение почти свободно?


Удивительно, но std::vector<char> компилируется вниз до того, что я ожидал.

Реализация clang сильно отличается, хотя есть подозрительное std::string::reserve

Ответы [ 2 ]

0 голосов
/ 30 мая 2018

Я только проанализировал версию GCC.Вот что происходит: код обрабатывает различные типы распределителей.Если у распределителя есть черта _S_propagate_on_move_assign или _S_always_equal, то движение почти бесплатное, как вы ожидаете.Это if в ходу operator=:

if (!__str._M_is_local()
    && (_Alloc_traits::_S_propagate_on_move_assign()
      || _Alloc_traits::_S_always_equal()))
          // cheap move
else assign(__str);

Если условие истинно (_M_is_local() означает небольшую строку, описание здесь ), то ход будет дешевым.

Если оно ложно, то оно вызывает normal assign (не движущийся).Это тот случай, когда либо:

  • строка мала, поэтому assign сделает простой memcpy (дешевый)
  • , либо у распределителя нет черты всегда равно или распространять-на-ходу-назначать , поэтому назначение будет выделять (недешево)

Что этозначит?

Это означает, что если вы используете распределитель по умолчанию (или любой распределитель с чертами, упомянутыми ранее), то ход все еще почти свободен .

НаС другой стороны, сгенерированный код неоправданно огромен и, я думаю, его можно улучшить.Он должен иметь отдельный код для обработки обычных распределителей или иметь лучший код assign (проблема в том, что assign не проверяет _M_is_local(), но выполняет проверку емкости, поэтому компилятор не может решить,Выделение необходимо или нет, поэтому он вставляет кодовый путь выделения в исполняемый файл без необходимости - вы можете проверить точные детали в исходном коде).

0 голосов
/ 30 мая 2018

Не совсем ответ, но это новая реализация C ++ 11 std::string без счетчика ссылок и с небольшой оптимизацией строк, что вызывает объемную сборку.В частности, небольшая строковая оптимизация заставляет 4 ветви обрабатывать 4 различные комбинации длин источника и цели назначения перемещения.

Когда добавлена ​​опция -D_GLIBCXX_USE_CXX11_ABI=0 для использования пре-C ++ - 11 std::stringсо счетчиком ссылок и без малой оптимизации строки код сборки выглядит намного лучше .


разве я не думаю, что перемещение почти свободно?

В Нет ничего лучше, чем Копировать или Переместить Роджер Орр talk, слайды стр. 47 гласит:

Сравнение копирования и перемещения

  • Многие люди ошибочно считают перемещение эффективным «свободным»
  • Разница в производительности между копированием и перемещением сильно варьируется
  • Для примитивного типа, такого как int, копирование или перемещениефактически идентичны
  • Перемещение выполняется быстрее, чем при копировании, когда требуется передать только часть объекта для передачи всего значения
...