Подтвердить что ты не робот

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

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

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) 
    {}
    ...
};

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

Позвольте называть это copy-then-move на X *; две операции: COPY, MOVE.

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

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

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

Может быть (ясно?) есть что-то, чего я не хватает в его разговоре?

4b9b3361

Ответ 1

Идея "Хоть скорость? Передача по значению" (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. Поэтому мы делаем копию, никоим образом не обходим ее.

Короче говоря, результат:

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

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

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

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


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

В качестве альтернативы исходное содержимое может быть доступно через машину обратного пути.

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

Ответ 2

Нет общих правил для optmization. Pass-by-value может дать большие выигрыши в С++ 11 с семантикой перемещения, наряду с копией elision.

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

Ответ 3

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

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