Быстрее ли вернуть значение или изменить параметр, переданный по ссылке?

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

void foo() {
  ImageType img = getCustomImage();
}

ImageType getCustomImage() {
  ImageType custom_img;
  //lots of code
  return custom_img;
}

Насколько мне известно, строка ImageType img = getCustomImage(); приведет к вызову конструктора копирования для img с возвращаемым значением из custom_img в качестве параметра. Википедия говорит, что некоторые компиляторы даже проделывают эту операцию снова, для исходной временной переменной!

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

void foo() {
  ImageType img;
  getCustomImage(img);
}

void getCustomImage(ImageType &img) {
  //code operating directly on img
}

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


person Arrakis    schedule 03.11.2011    source источник
comment
Почему вы отметили это буквой C? С вашими упоминаниями конструкторов копирования и вашего синтаксиса для передачи по ссылке в сигнатуре функции getCustomImage это применимо только к C++.   -  person Vicky    schedule 03.11.2011
comment
В зависимости от ваших конструкторов ImageType, даже без оптимизации возвращаемого значения, эти две формы могут быть более или менее эквивалентны. Вот почему единственный правильный ответ на вопрос, что быстрее, — профиль. Если вы не можете работать со всеми платформами, выберите свою любимую... основные проблемы, скорее всего, будут универсальными.   -  person Dennis Zickefoose    schedule 03.11.2011
comment
Чтобы пойти по касательной, я бы сказал, что хороший подход, который позволит обойти все ваши заботы, - это просмотреть определение ImageType. Если бы этот класс стал небольшим, легким обработчиком для динамически управляемого хранилища (скажем, членом vector), и если вы используете семантику перемещения C++11, то вы могли бы оставить свой код как есть (возврат по значению) и вы получите хорошую производительность.   -  person Kerrek SB    schedule 03.11.2011


Ответы (4)


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

Вы правы в том, что логически код запускает разные конструкции копирования: от custom_img к возвращаемому временному объекту, а затем к объекту img в коде вызывающего объекта, но факт в том, что обе копии будут опущены.

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

Я писал об этом (семантика значений в аргументах функций и возвращаемых значениях) в прошлом в этих двух записях блога:

EDIT: я намеренно избегал обсуждения случаев, когда NRVO не может быть применен компилятором, по той причине, что любая функция f, которая может получить ссылку на объект для обработки: void f( T & out ) { /* code */ } может быть тривиально преобразована в функцию, в которой компилятор тривиально реализует NRVO, которая возвращает значение путем простого преобразования в: T f() { T out; /* code */ return out; }

person David Rodríguez - dribeas    schedule 03.11.2011
comment
Как вы сказали, но никто не выразился лучше, чем Дональд Кнут: мы должны забыть о малой эффективности, скажем, примерно в 97% случаев: преждевременная оптимизация — корень всех зол. :) - person Some programmer dude; 03.11.2011
comment
@JoachimPileborg: Я не совсем уверен, что ваша цитата точно соответствует тому, что сказал Дэвид, это связано, но ... Я ожидаю, что программисты определят свои собственные причудливые идеи как попадающие в оставшиеся 3%. - person Matthieu M.; 03.11.2011
comment
Я бы добавил, что в большинстве случаев сконструированный по умолчанию объект не имеет смысла. Предполагается, что конструктор инициализирует инварианты класса, и поэтому большинство конструкторов по умолчанию являются ошибками дизайна (не лучше, чем нулевые указатели: печально известная ошибка на миллиард долларов). Когда вам дается объект, вы ожидаете, что он будет готов к использованию, это контракт конструктора. Конструкторы по умолчанию (например, нулевые указатели) нарушают этот контракт. А без конструктора по умолчанию эта идиома out-параметров невозможна (они становятся in/out). - person Matthieu M.; 03.11.2011
comment
@JoachimPileborg: продолжая эту цитату: все же мы не должны упускать наши возможности в этих критических 3%. Хорошего программиста такие рассуждения не убаюкают, ему будет мудро внимательно посмотреть на критический код; но только после того, как этот код был идентифицирован. - person Mike Seymour; 03.11.2011
comment
@MatthieuM. полностью согласен, и не только это, но даже в тех случаях, когда конструкция по умолчанию могла бы иметь смысл (вспомните функцию, возвращающую вектор элементов, пустой вектор — это правильно полностью построенный объект), даже в тех случаях она усложняет код. Функция не контролирует, является ли вектор, переданный в качестве ссылки, пустым или нет, поэтому для соблюдения его пост-условий она должна обрабатывать непустой случай путем очистки, что потребует дополнительного кода для поддержки и выполнения. - person David Rodríguez - dribeas; 04.11.2011

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

Я больше знаком с C, чем с C++, поэтому могу ошибаться.

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

person Basile Starynkevitch    schedule 03.11.2011
comment
Если используются указатели, они должны быть своего рода умными указателями. - person balki; 03.11.2011
comment
Вернемся к исходному вопросу: Qt — это библиотека C++ с классом QImage. - person Basile Starynkevitch; 03.11.2011
comment
@James: Потому что необработанные указатели провоцируют утечки в вашем коде, пока он не умрет ужасно. - person Matthieu M.; 03.11.2011
comment
@Matthieu M: Меня интересует часть претензии «необходимость». - person James; 03.11.2011
comment
@James: я бы сказал, что это резкое заявление, и его можно уточнить. Однако на практике необработанные указатели вызывают сомнения: разработчик использовал необработанный указатель, потому что он не владеет ресурсом, или он забыл о нашем соглашении, и на самом деле он владеет ресурсом? Систематическое использование умного указателя (и наличие тупого класса pointer) позволяет четко документировать в коде принадлежность не только его вида (когда он есть), но и его отсутствия. - person Matthieu M.; 03.11.2011

По крайней мере, если вы ориентируетесь на достаточно современные компиляторы для достаточно типичных ОС, таких как Windows, MacOS, Linux или *BSD, вы вполне можете рассчитывать на их реализацию RVO/NRVO. IOW, вам пришлось бы очень постараться, чтобы найти случаи, в которых было бы достаточно различий, о которых нужно было бы заботиться, или, скорее всего, вообще.

В зависимости от того, как вы используете задействованные данные, если есть разница в скорости, это может так же легко способствовать передаче/возврату объектов, как и использование ссылки. Вы можете прочитать статью Дэвида Абрахамса. об этом.

person Jerry Coffin    schedule 03.11.2011

Увидев вопрос "Что быстрее?", я вообще советую реально померить для себя, в своем компиляторе/окружении, а потом выяснить, почему так.

person Violet Giraffe    schedule 03.11.2011