C ++: может ли компилятор оптимизировать передачу по значению?

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

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

Вот пример. Предположим, у нас есть следующая функция:

std::vector<Foo> modify(std::vector<Foo> data) {
    /* Do some funny things to data */
    return data;
}

Затем эта функция используется следующим образом:

std::vector<Foo> bigData = /* big data */;
bigData = modify(bigData); // Here copying the data into the function could be omitted

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

Обновить

Учтем C ++ 11. Интересно, верно ли следующее понимание: если значение, переданное параметру функции по значению, является r-значением, а тип параметра имеет конструктор перемещения, конструктор перемещения будет использоваться вместо конструктора копирования.

Например:

std::vector<Foo> bigData = /* big data */;
bigData = modify(std::move(bigData));

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


person bweber    schedule 09.06.2017    source источник
comment
Трудно ответить окончательно, но если бы мне пришлось держать пари: я не думаю, что вы когда-нибудь получите эту оптимизацию , если функция не встроена.   -  person Nir Friedman    schedule 09.06.2017
comment
Трудно ответить однозначно - ну, это довольно просто, просто нужно взять интересующий вас компилятор и посмотреть на сгенерированный asm.   -  person UKMonkey    schedule 09.06.2017
comment
@UKMonkey Легко ответить, для конкретной функции, для конкретного компилятора, для конкретной архитектуры, для определенных флагов, да, я согласен. Вопрос гораздо более общий, и тело функции не определено, поэтому я бы не сказал, что это просто. Как бы то ни было, я приложил все усилия, чтобы ответить.   -  person Nir Friedman    schedule 09.06.2017


Ответы (3)


В C ++ 11 компилятор может определить, что bigData переназначен после использования в функции, и передать его как rvalue, но для этого нет гарантии, в отличие от RVO (из C ++ 17).

По крайней мере, для std::vector вы можете убедиться, что это произойдет, вызвав функцию как modify(std::move(bigData)), которая построит значение в modify из ссылки rvalue, которую нельзя оптимизировать с помощью RVO afaik, потому что это параметр функции, который явно исключен из эта оптимизация (третий пункт здесь). Однако компилятор должен понимать, что возвращаемое значение является r-значением, и снова переместить его в большие данные.

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

person midor    schedule 09.06.2017

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

__attribute__((noinline)) std::vector<double> modify(std::vector<double> data) {
    std::sort(data.begin(), data.end());
    return data;
}

std::vector<double> blah(std::vector<double> v) {
    v = modify(v);
    return v;
}

Вы можете посмотреть сгенерированную для этого сборку для различных компиляторов; вот у меня clang 4.0 с оптимизацией O3: https://godbolt.org/g/xa2Dhf. Если вы внимательно посмотрите на сборку, вы увидите вызов operator new в blah. Это доказывает, что blah действительно выполняет копирование для вызова модификации.

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

person Nir Friedman    schedule 09.06.2017

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

Прочтите этот пост (Оптимизирует ли компилятор параметры функции, переданные по значению?), которые, я надеюсь, могут дать вам лучшее представление.

person iamrameshkumar    schedule 09.06.2017