Оптимизация возвращаемого значения C ++

Этот код:

#include <vector>

std::vector<float> getstdvec() {
std::vector<float> v(4);

v[0] = 1;
v[1] = 2;
v[2] = 3;
v[3] = 4;

return v;
}

int main() {
std::vector<float> v(4);

for (int i = 0; i != 1000; ++i)
{
v = getstdvec();
}
}

Я неверно понимаю, что функция getstdvec не должна фактически выделять вектор, который она возвращает. Когда я запускаю это в valgrind / callgrind, я вижу 1001 вызов malloc; 1 для начального объявления вектора в main и 1000 для каждой итерации цикла.

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

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

9

Решение

Когда вы вызываете функцию, для типа возврата, такого как std::vector<T> компилятор предоставляет память для возвращаемого объекта. Вызываемая функция отвечает за создание экземпляра, который она возвращает в этом слоте памяти.

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

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

Это означает, что RVO / NRVO работает только при вызове функции для создания нового экземпляра, а не при его назначении. Ниже приведен пример использования RVO / NRVO:

std::vector<float> v = getstdvec();

но исходный код использует цикл, и в каждой итерации результат из getstdvec() должен быть построен, и этот временный v, Нет никакого способа, которым RVO / NRVO мог бы удалить это.

16

Другие решения

Вы можете передать его по ссылке … copy elision делает это так, что v = getstdvect () выделяет v (в вашем main) непосредственно v (в вашем getstdvec ()) и пропускает копию, обычно связанную с возвратом по значению, но это НЕ будет пропускать v (4) в вашей функции. Чтобы сделать это, вам нужно взять вектор по ссылке:

#include <vector>
void getstdvec(std::vector<float>& v){
v.resize(4);//will only realocate if v is wrong size
v[0] = 1; v[1] = 2; v[2] = 3; v[3] = 4;
return v;
}
int main() {
std::vector<float> v(4);
for (int i=0; i!=1000;++i)
getstdvec(v);
}
3

Вы выполняете копирование в вашем цикле, а не копирование. Оптимизация RVO применяется только к построению переменных из возвращаемого значения, а не их присвоению.

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

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

2

Самый простой ответ — передать уже созданный векторный объект в функцию.

std::vector<float> getstdvec(std::vector<float> &myvec){

В этом случае вам не нужно возвращать его так

void getstdvec(std::vector<float> &myvec){
1

вместо того, чтобы использовать возвращаемое значение, вы можете использовать ссылку:

void getstdvec(std::vector<float> &v)

Который может избежать копирования временного объекта

1

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

По-вашему, вы объявляете локальный вектор размером 4, поэтому каждый раз, когда вызывается функция, она выделяет память. Если вы имеете в виду, что вы всегда изменяете один и тот же вектор, вы можете вместо этого передать вектор по ссылке.

Например:

void getstdvec(std::vector<float>& vec)
{                                //^^
//do something with vec
}

внутри main, вы объявляете вектор и выделяете пространство как то, что вы сделали. Теперь вы делаете следующее:

for (int i=0; i!=1000;++i)
{        //^^^minor: Don't use magic number in the code like this,
//define a const instead
getstdvec(vec);
}
1
По вопросам рекламы [email protected]