Иногда утверждают, что C ++ 11/14 может повысить производительность даже при простой компиляции кода C ++ 98. Обоснование обычно происходит в соответствии с семантикой перемещения, поскольку в некоторых случаях конструкторы rvalue генерируются автоматически или теперь являются частью STL. Теперь мне интересно, были ли эти случаи ранее уже обрабатывались RVO или подобными оптимизациями компилятора.
Тогда у меня возникает вопрос: не могли бы вы привести пример кода на C ++ 98, который без изменений работает быстрее с использованием компилятора, поддерживающего новые возможности языка. Я понимаю, что стандартный компилятор не требует выполнения копирования, и именно по этой причине семантика перемещения может привести к скорости, но я хотел бы увидеть менее патологический случай, если хотите.
РЕДАКТИРОВАТЬ: Просто чтобы прояснить, я не спрашиваю, быстрее ли новые компиляторы, чем старые компиляторы, а скорее, если есть код, добавляющий -std = c ++ 14 к моим флагам компилятора, он будет работать быстрее (избегайте копий, но если вы может придумать что-нибудь еще кроме семантики перемещения, мне тоже будет интересно)
источник
std::move
и перемещения конструкторов (что потребует внесения изменений в существующий код). Единственное, что действительно касалось моего вопроса, было предложение «Вы получаете немедленные преимущества в скорости просто путем перекомпиляции», которое не подкреплено какими-либо примерами (там упоминается STL на том же слайде, как я это сделал в своем вопросе, но ничего конкретного ). Я просил несколько примеров. Если я неправильно читаю слайды, дайте мне знать.Ответы:
Мне известны 5 общих категорий, в которых перекомпиляция компилятора C ++ 03 в C ++ 11 может привести к неограниченному увеличению производительности, которое практически не связано с качеством реализации. Это все вариации семантики перемещения.
std::vector
перераспределятькаждый раз , когда
foo
буфер «s перераспределяется в C ++ 03 он копируется каждыйvector
вbar
.В C ++ 11 вместо этого перемещается
bar::data
s, что в основном бесплатно.В этом случае это зависит от оптимизации внутри
std
контейнераvector
. В каждом случае ниже использованиеstd
контейнеров только потому, что они являются объектами C ++, которые имеют эффективнуюmove
семантику в C ++ 11 «автоматически» при обновлении компилятора. Объекты, которые не блокируют его и содержатstd
контейнер, также наследуют автоматически улучшенныеmove
конструкторы.НРВО провал
Когда NRVO (оптимизация именованного возвращаемого значения) завершается неудачно, в C ++ 03 он возвращается к копии, а в C ++ 11 - к перемещению. Неудачи НРВО легки:
или даже:
У нас есть три значения - возвращаемое значение и два разных значения внутри функции. Elision позволяет объединять значения в функции с возвращаемым значением, но не друг с другом. Они оба не могут быть объединены с возвращаемым значением без объединения друг с другом.
Основная проблема заключается в том, что элиминация NRVO является хрупкой, и код с изменениями, не находящимися рядом с
return
сайтом, может внезапно привести к значительному снижению производительности в этом месте без использования диагностической информации. В большинстве случаев сбоя NRVO C ++ 11 заканчиваетсяmove
копией, а C ++ 03 заканчивается копией.Возврат аргумента функции
Исключение также невозможно здесь:
в C ++ 11 это дешево: в C ++ 03 нет способа избежать копирования. Аргументы функций не могут быть исключены с возвращаемым значением, потому что время жизни и местоположение параметра и возвращаемого значения управляются вызывающим кодом.
Тем не менее, C ++ 11 может переходить от одного к другому. (В менее игрушечном примере что-то может быть сделано с
set
).push_back
илиinsert
Наконец, исключение в контейнеры не происходит: но C ++ 11 перегружает rvalue, перемещает операторы вставки, что сохраняет копии.
в C ++ 03 создается временный объект
whatever
, затем он копируется в векторv
.std::string
Выделено 2 буфера, каждый с одинаковыми данными, а один отбрасывается.В C ++ 11 временный
whatever
создается.whatever&&
push_back
Перегрузки , тоmove
с , что временная в векторv
. Одинstd::string
буфер выделяется и перемещается в вектор. Пустоеstd::string
отбрасывается.присваивание
Украденный из ответа @ Jarod42 ниже.
Исключение не может произойти с назначением, но может произойти.
здесь
some_function
возвращает кандидата для исключения, но поскольку он не используется для непосредственного создания объекта, он не может быть исключен. В C ++ 03 вышеприведенное приводит к тому, что содержимое временного объекта копируется вsome_value
. В C ++ 11 он перемещенsome_value
, что в основном бесплатно.Для полного эффекта вышеперечисленного вам нужен компилятор, который синтезирует конструкторы перемещения и присваивания для вас.
MSVC 2013 реализует конструкторы перемещения в
std
контейнерах, но не синтезирует конструкторы перемещения для ваших типов.Таким образом, типы, содержащие
std::vector
s и подобные, не получат таких улучшений в MSVC2013, но начнут получать их в MSVC2015.clang и gcc уже давно реализовали неявные конструкторы перемещения. Компилятор Intel 2013 года будет поддерживать неявную генерацию конструкторов перемещения, если вы пропустите
-Qoption,cpp,--gen_move_operations
(по умолчанию они этого не делают, пытаясь обеспечить кросс-совместимость с MSVC2013).источник
std
контейнеры библиотеки будут обновлятьсяmove
конструкторами «бесплатно» и (если вы не блокировали это) конструкции, использующие указанные объекты ( и упомянутые объекты) начнут получать свободное перемещение в ряде ситуаций. Многие из этих ситуаций рассматриваются в C ++ 03: не все.std
контейнеров, приведенных выше, в основном объясняется тем, что они являются дешевыми в использовании, так как копируют тип, который вы получаете «бесплатно» в C ++ 11 при перекомпиляции C ++ 03.vector::resize
Является исключением: он используетmove
в C ++ 11.если у вас есть что-то вроде:
Вы получили копию в C ++ 03, тогда как вы получили задание на перемещение в C ++ 11. так что у вас есть бесплатная оптимизация в этом случае.
источник
foo().swap(v);