Может ли современный C++ получить вам производительность бесплатно?
иногда утверждается, что C++11/14 может повысить производительность даже при простой компиляции кода C++98. Обоснование обычно выполняется в соответствии с семантикой перемещения, так как в некоторых случаях конструкторы rvalue автоматически генерируются или теперь являются частью STL. Теперь мне интересно, были ли эти случаи ранее уже обработаны RVO или аналогичными оптимизациями компилятора.
мой вопрос тогда, если бы вы могли дать мне фактический пример части C++98 код, который без изменений работает быстрее с помощью компилятора, поддерживающего новые функции языка. Я понимаю, что стандартный соответствующий компилятор не требуется для выполнения копирования elision и только по этой причине семантика перемещения может привести к скорости, но я хотел бы увидеть менее патологический случай, если хотите.
EDIT: просто чтобы быть ясным, я не спрашиваю, являются ли новые компиляторы быстрее старых компиляторов, а скорее, если есть код, в котором добавление-std=c++14 к моим флагам компилятора он будет работать быстрее (избегайте копий, но если вы можете придумать что-нибудь еще, кроме семантики перемещения, мне тоже будет интересно)
2 ответа:
я знаю 5 общих категорий, где перекомпиляция компилятора C++03 Как C++11 может привести к неограниченному увеличению производительности, которые практически не связаны с качеством реализации. Это все вариации семантики перемещения.
std::vector
перераспределятьstruct bar{ std::vector<int> data; }; std::vector<bar> foo(1); foo.back().data.push_back(3); foo.reserve(10); // two allocations and a delete occur in C++03
каждый раз
foo
буфер перераспределяется в C++03 он копируется каждыйvector
наbar
.в C++11 он вместо этого перемещает
bar::data
s, который в основном бесплатный.в этом случае это зависит от оптимизации внутри
std
контейнерvector
. В каждом случае ниже, использованиеstd
контейнеры просто потому, что они являются объектами C++, которые имеют эффективныйmove
семантика в C++11 "автоматически" при обновлении компилятора. Объекты, которые не блокируют его, которые содержатstd
контейнер также наследует автоматическое улучшениеmove
конструкторы.NRVO отказ
когда NRVO (по имени return оптимизация значения) не удается, в C++03 он возвращается на копию, на C++11 он возвращается на ход. Неудачи НРВО легки:
std::vector<int> foo(int count){ std::vector<int> v; // oops if (count<=0) return std::vector<int>(); v.reserve(count); for(int i=0;i<count;++i) v.push_back(i); return v; }
или еще:
std::vector<int> foo(bool which) { std::vector<int> a, b; // do work, filling a and b, using the other for calculations if (which) return a; else return b; }
у нас есть три значения -- возвращаемое значение и два разных значения внутри функции. Elision позволяет объединить значения внутри функции с возвращаемым значением, но не друг с другом. Они оба не могут быть объединены с возвращаемым значением без слияния друг с другом.
базовый проблема в том, что nrvo elision является хрупким, а код с изменениями не рядом с
return
место может внезапно иметь массивные уменьшения представления на том пятне без испущенной диагностики. В большинстве случаев отказа NRVO C++11 заканчивается наmove
, в то время как C++03 заканчивается копией.возврат аргумента функции
Элизия здесь тоже невозможна:
std::set<int> func(std::set<int> in){ return in; }
в C++11 это дешево: в C++03 нет способа избежать копирования. Аргументы для функций невозможно удалить возвращаемое значение, поскольку время жизни и расположение параметра и возвращаемого значения управляются вызывающим кодом.
однако, C++11 может переходить от одного к другому. (В менее игрушечном примере что-то может быть сделано с
set
).
push_back
илиinsert
наконец elision в контейнеры не происходит: но C++11 перегружает операторы вставки перемещения rvalue, что сохраняет копии.
struct whatever { std::string data; int count; whatever( std::string d, int c ):data(d), count(c) {} }; std::vector<whatever> v; v.push_back( whatever("some long string goes here", 3) );
в C++03 a временно
whatever
создается, затем копируется в векторv
. 2std::string
буферы выделяются, каждый с идентичными данными, и один отбрасывается.в C++11 временный это. Элемент
whatever&&
push_back
тонкостиmove
s, что временно в векторv
. Одинstd::string
буфер выделяется и перемещается в вектор. Пустойstd::string
отбрасывается.задание
украдено из ответа @Jarod42 под.
Elision не может произойти с назначением, но move-from может.
std::set<int> some_function(); std::set<int> some_value; // code some_value = some_function();
здесь
some_function
возвращает кандидата в elide from, но поскольку он не используется для непосредственного построения объекта, он не может быть elide. В 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).