Насколько верно «Хочу скорости»? Передать по значению

Поправьте меня если я ошибаюсь. Скажем, у меня есть:

struct X
{
    std::string mem_name;

    X(std::string name)
        : mem_name(std::move(name)) 
    {}
    ...
};
struct Y
{
    std::string mem_name;

    Y(const std::string &name)
        : mem_name(name) 
    {}
    ...
};

В ctor X name, очевидно, является копией любого аргумента, переданного X, X вызывает ctor перемещения std::string для инициализации mem_name, верно?

Назовем это копировать-затем-переместить на X*; две операции: КОПИРОВАНИЕ, ПЕРЕМЕЩЕНИЕ.

В ctor Y name является константной ссылкой, что означает, что фактической копии элемента нет, потому что мы имеем дело непосредственно с аргументом, переданным из того места, где должен быть создан объект Y. Но затем мы скопировали name, чтобы инициализировать mem_name в Y; одна операция: КОПИРОВАНИЕ. Конечно, поэтому он должен быть намного быстрее (и предпочтительнее для меня)?

В докладе Скотта Мейера о GN13 (примерно 8:10 и 8:56) он говорит о "Хотите скорости? Передавайте по значению", и мне интересно, есть ли разница в производительности или потери при передаче аргументы (или строки, если быть точным) по ссылке и передаче по значению "для увеличения скорости?"

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

Может быть (очевидно?) я что-то упускаю из его речи?


person iamOgunyinka    schedule 06.02.2014    source источник
comment
Звучит как классический компромисс между памятью и процессором, но я не вижу, где происходит ускорение. Приходится копировать, на что уходит время. Но у вас нет проблем с конфликтами потоков с отдельными копиями. Он говорил в контексте асинхронных задач›?   -  person duffymo    schedule 06.02.2014
comment
Вы можете прочитать исходную статью также. Кстати, я немного улучшил форматирование вашего сообщения, но вы, возможно, захотите потратить больше времени на форматирование и отступы (проверив это в предварительном просмотре).   -  person Angew is no longer proud of SO    schedule 06.02.2014
comment
@duffymo Идея состоит в том, что иногда копию можно исключить с помощью прямой конструкции на месте при взятии по значению, но это невозможно при взятии по константной ссылке.   -  person Angew is no longer proud of SO    schedule 06.02.2014
comment
Нет, это было, когда он объяснял, как работает std::move, когда обсуждал перемещение из константного объекта.   -  person iamOgunyinka    schedule 06.02.2014
comment
Подробное объяснение возможностей оптимизации, предоставляемых передачей по значению, можно найти на отличном BoostCon Чендлера Каррута. Основной доклад 2013 года (видео на YouTube).   -  person ComicSansMS    schedule 06.02.2014


Ответы (3)


Идея "Хотите скорость? Передавайте по значению"(1) заключается в том, что иногда копию можно опустить. Принимая ваши классы X и Y, рассмотрите этот вариант использования:

// Simulating a complex operation returning a temporary:
std::string foo() { return "a" + std::string("b"); }


struct X
{
  std::string mem_name;
  X(std::string name): mem_name(std::move(name)) {}
};

struct Y
{
  std::string mem_name;
  Y(const std::string &name): mem_name(name) {}
};


int main()
{
  X(foo());
  Y(foo());
}

Теперь давайте проанализируем оба случая строительства.

X первый. foo() возвращает временное значение, которое используется для инициализации объекта name. Затем этот объект перемещается в mem_name. Обратите внимание, что компилятор может применить оптимизацию возвращаемого значения и создать возвращаемое значение foo() (фактически даже возвращаемое значение operator+) непосредственно в пространстве name. Так что на самом деле никакого копирования не происходит, только перемещение.

Теперь давайте проанализируем Y. foo() снова возвращает временное значение, привязанное к ссылке name. Теперь для возвращаемого значения нет «внешнего» пространства, поэтому оно должно быть создано в своем собственном пространстве и привязано к ссылке. Затем он копируется в mem_name. Итак, мы делаем копию, и никак иначе.

Короче итог такой:

  • Если передается lvalue, оба X и Y будут выполнять копирование (X при инициализации name, Y при инициализации mem_name). Кроме того, X выполнит ход (при инициализации mem_name).

  • Если передается rvalue, X потенциально выполнит только перемещение, а Y должно выполнить копирование.

Как правило, ожидается, что перемещение будет операцией, требующей времени, сравнимой с затратами времени на передачу указателя (что и делает передача по ссылке). Таким образом, X не хуже, чем Y для lvalue, и лучше для rvalue.

Конечно, это не абсолютное правило, и к нему нужно относиться с недоверием. Если есть сомнения, профиль.


(1) Ссылка может быть временно недоступна, и по состоянию на 12.11.2014 она кажется неработающей (404). Копия содержимого (хотя и со странным форматированием) кажется доступной на нескольких сайтах блогов:

Кроме того, исходный контент может быть доступен через обратная машина.

Также отметим, что тема вообще вызвала немало дискуссий. Погуглив название статьи, можно найти множество дополнений и контраргументов. Чтобы перечислить пример одного из них, есть "Хотите скорость? Не (всегда) передавать по значению" от члена SO juanchopanza

person Angew is no longer proud of SO    schedule 06.02.2014
comment
было бы здорово найти новую ссылку, сейчас 404. - person v.oddou; 11.12.2014
comment
@v.oddou :-( Я не могу найти его сейчас; если вы или кто-то еще наткнетесь на него, не стесняйтесь редактировать его. - person Angew is no longer proud of SO; 11.12.2014
comment
Я обнаружил, что: m.blog.csdn.net/blog/CPP_CHEN/17528669 и некоторые другие авторы для смягчения последствий: juanchopanzacpp.wordpress.com/2014/05/11/ - person v.oddou; 11.12.2014
comment
@v.oddou Спасибо, добавлено. - person Angew is no longer proud of SO; 11.12.2014
comment
comment
и статья только что была удалена с машины обратного пути из-за файла robots.txt ... - person Giel; 15.06.2016
comment
То, что я считаю другой копией, можно найти здесь (думаю, потому что я давно не читал оригинал): joseluisestebanaparicio.blogspot.nl/2010/06/ - person Giel; 15.06.2016
comment
У меня есть вопрос. Во-первых, я говорю о C++11. Согласно правилу пяти, при объявлении конструктора копирования мы также должны объявить конструктор перемещения. В таком случае struct Y строится из lvalue с операцией копирования и из rvalue только с операцией перемещения, что дает как минимум такую ​​же производительность по сравнению с struct X (или даже лучше в случае lvalue для исключения операции перемещения). Тогда рекомендуется ли передача по значению? - person jerry_fuyi; 24.07.2019
comment
@jerry_fuyi Код в ответе не имеет отношения к передаче X или Y где-то, он имеет дело с передачей std::string где-то. Конечно, перегрузка для const std::string& и std::string&& будет самым безопасным и эффективным способом. , но это дополнительный код, и если ваша функция имеет больше таких параметров, она взрывается комбинаторно. Идея состоит в том, что функция получения значения работает почти так же хорошо, как перегруженная пара lvalue+rvalue. - person Angew is no longer proud of SO; 24.07.2019
comment
@Angew Я понимаю. Конструктор, принимающий std::string, не относится к правилу трех/пяти. Спасибо. - person jerry_fuyi; 24.07.2019
comment
Статья по-прежнему доступна здесь: archive.is/ROtoH - person Michał Góral; 18.02.2020

Общих правил оптимизации нет. Передача по значению может дать большие преимущества в C++11 с семантикой перемещения, наряду с исключением копирования.

Если вам действительно нужна скорость, профилируйте свой код.

person cdmh    schedule 06.02.2014

Если вы действительно не возражаете против выставления ссылок в вашем API (что ДОЛЖНО БЫТЬ признаком того, что внутри вы будете копировать/назначать данный объект), то использование копий в порядке.

Удаление копирования происходит быстрее, чем перемещение, и если его нельзя исключить (по разным причинам, например, слишком длинная цепочка вызовов зависимых вызовов функций), то C++ гарантирует семантику перемещения.

person Red XIII    schedule 06.02.2014
comment
+1 за упоминание эффекта (и последствий) в общедоступном API. - person Angew is no longer proud of SO; 06.02.2014