Может ли современный 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::datas, который в основном бесплатный.в этом случае это зависит от оптимизации внутри
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тонкостиmoves, что временно в вектор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::vectors и подобные не получают таких улучшений в MSVC2013, но начнут получать их в MSVC2015.clang и gcc уже давно реализовали неявные конструкторы перемещения. Компилятор Intel 2013 будет поддерживать неявную генерацию конструкторов перемещения, если вы пас
-Qoption,cpp,--gen_move_operations(они не делают это по умолчанию, чтобы быть кросс-совместимыми с MSVC2013).