Является ли преждевременной оптимизацией использование std:: move()?


Предположим, у меня есть следующий код:

int main()
{
    std::vector<std::string> strs;

    std::string var("Hello World");
    // Make some modifications to 'var'
    strs.push_back(std::move(var));
}

часть образца, на которую я хочу указать, - это использование std::move(). В основном я беспокоюсь о копии на push_back() звонок. Предположим, что строка, которую я добавляю, действительно большая. Я все еще изучаю ссылки на r-значения C++11, поэтому я не уверен, как компилятор оптимизирует копию (если вообще) без std::move().

может кто-нибудь объяснить, если это преждевременная оптимизация (заставляя движется во всех случаях, когда вы хотите избежать копий, в общем)? Если да, то какие шаблоны я должен ожидать, что компилятор будет следовать (или, скорее всего, следовать), что приведет к оптимизированному и автоматическому перемещению здесь?

EDIT

Я хочу добавить, что я понимаю, как автоматические движения происходят на функция возвращает значения, потому что nrvo / RVO применяется. Конкретный пример, который я привел здесь, не будет применяться RVO, поэтому я не уверен.

5 63

5 ответов:

другие ответы слишком много внимания на технические аспекты этого вопроса, на мой вкус, поэтому я постараюсь дать более общий ответ.

короче говоря: нет, использование "трюка", такого как std:: move так, как это описано в вопросе, не является преждевременной оптимизацией. Не используя std::move, когда его можно использовать-это тоже хорошо, если код уже известно, критическое для производительности.

необходимость избегать преждевременной оптимизации иногда понимается как "не оптимизируйте, пока не докажете, что это необходимо", но я предпочитаю читать его как: "не тратьте время на решение проблем, если вы не знаете, что их нужно решить".

преждевременная оптимизация требует затрат усилий для того, чтобы оптимизировать то, что может не нуждаться в оптимизации, и обычно превращает простую проблему в сложную проблему при этом. С этой точки зрения я бы классифицировал любое длительное размышление над самим вопросом как преждевременную оптимизацию.

связанные с пример: люди, работающие в критическом для производительности коде, часто передают Аргументы в виде ссылок const ( const std::string&). Поскольку это то, что они привыкли делать, они будут использовать тот же шаблон в коде, который не критичен для производительности, даже если они могут просто использовать pass-by-copy ( const std::string, или даже std::string). Это тоже не преждевременная оптимизация.

Я не уверен, как компилятор будет оптимизировать копию (если вообще) без std::move().

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

без перемещения в код фактически представляет собой последовательность вызовов:

strlen  // count "Hello World"
malloc  // allocate memory for string var
strcpy  // copy data into var
malloc  // re-allocate vector
free    // deallocate old vector
malloc  // allocate new string
strcpy  // copy from var to new string
free    // destroy var

С ходом он становится:

strlen  // count "Hello World"
malloc  // allocate memory for string var
strcpy  // copy data into var
malloc  // re-allocate vector
free    // deallocate old vector

теоретически умный компилятор может сделайте это преобразование автоматически, но для компилятора, чтобы увидеть через все слои абстракции, введенные конструкторами и деструктором и vector функции-члены довольно сложно, поэтому доказать, что код может быть преобразован для удаления malloc и free сложная.

после std::move исходный объект, в данном случае var, должно быть в допустимом состоянии, но может содержать любое значение, например, оно может быть пустым.

если вы знаете, что вы не собираетесь использовать var и вы только создали его, чтобы положить в vector тогда это не совсем "преждевременная" оптимизация, как намерение того, что вы пытаетесь сделать.

vector новый способ emplace_back() что то же самое, но яснее, и использует forward-аргументы (здесь вы просто делаете emplace_back("Hello World") если все, что вы делаете при его строительстве). В вашем случае, как вы "делаете некоторые изменения с var"emplace_back вряд ли будет неуместно.

старый C++ вы можете оптимизировать копию, сделав push_back() на пустую строку, а затем замена его.

Я не знаю, почему все предлагают вам использовать emplace_back(). Цель emplace_back() чтобы избежать операций копирования/перемещения по строительству объекта на месте. В этом случае, вы уже построили объект, так, по крайней мере, 1 копирование/перемещение неизбежно. Нет никакого преимущества в использовании emplace_back() over push_back() в этом случае.

в противном случае я согласен со всеми остальными, говоря, что это не преждевременная оптимизация, потому что семантика перемещения моделирует то, что вы пытаетесь сделать более тесно чем делать копию объекта.

Да, это преждевременная оптимизация, если это преждевременная оптимизация.

Позвольте уточнить:
Преждевременная оптимизация определяется тем, оптимизируете ли вы часть кода, которая не является критически важной для производительности. То есть преждевременная оптимизация никогда не определяется методом оптимизации, она всегда определяется местом оптимизации и вашими знаниями о том, что вы делаете.


теперь о целесообразности оптимизации используя std::move():

С std::move() вам избежать тяжелого подъема строительных копию, как выделение памяти, освобождение памяти, копию построения элементов, разрушение элементов, и т. д. Это хорошо. Но одна часть повторяется: строительство/разрушение объекта контейнера.

emplace_back() имеет преимущество полностью избежать строительства временного объекта. Таким образом, всякий раз, когда вы можете избежать временного объекта с помощью emplace_back(), у вас есть небольшой выиграть.

учитывая качество кода (читаемость/ремонтопригодность):std::move() имеет обратную сторону, оставляя вас с непригодным объектом, который может быть источником ошибок. Это не происходит с emplace_back(). Так что, опять же, последнее явно предпочтительнее.

конечно, emplace_back() не может использоваться во всех контекстах, которые позволяют использовать std::move(). Итак, если это критическая производительность,std::move() может быть, так и надо. Есть несколько очень допустимых вариантов использования для оптимизации с помощью std::move(). Тем не менее, вы также можете узнать, что вы можете написать код таким образом, что не требует никакого копирования/перемещения/emplace конструкции вообще. Никогда не прекращайте искать лучшее решение при оптимизации!

в конце концов, остается, что обоснованность оптимизации с std::move() полностью зависит от контекста: это хорошая оптимизация, где это хорошая оптимизация.