C++ 返回值优化RVO

一、原理

C++的返回值优化(Return Value Optimization,也被称为RVO)是一个重要的优化技术,可以提高程序的性能。返回值优化(RVO)是指编译器对临时变量的处理机制。对于函数返回一个对象的情况,编译器在生成代码时会按照以下规则进行优化:

1.如果返回值是一个非静态局部变量,并且该变量是通过复制返回(Return by Value)方式返回的,编译器会尝试将该返回值直接构造在调用方的返回值处,而不是返回一个副本。

2.如果返回值是一个匿名临时变量,编译器会优化掉这个临时变量的构造和析构函数,直接将其值传递给函数返回值的目标对象。

其中,第一种情况的优化被称为NRVO(Named Return Value Optimization,命名返回值优化或者具名返回值优化)

RVO的核心思想是:在函数内部创建的对象,在函数调用结束后会立即被销毁,这些临时对象的生命周期比较短暂,因此可以直接将它们构造在调用方的返回值处,避免产生额外的开销。

二、示例分析

static int counter = 0; // counter to identify instances
class Data {
 public:
  Data() : id(++counter) {
    std::cout << "ctor " << id << std::endl;
  }
  Data(const Data& other) : id(++counter) {
    std::cout << "copy ctor " << id << std::endl;
  }
  Data& operator=(const Data& other) {
    std::cout << "copy assign " << other.id << " to " << id << std::endl;
    return *this;
  }
  ~Data() {
    std::cout << "dtor " << id << std::endl;
  }
  int i = 0;
 private:
  int id = 0;
};

2.1、不具名返回值优化

不具名返回值优化发生在返回一个无名对象或者临时对象, 一般是 Return 语句中直接创建并返回的对象。由于编译器默认开启该优化选项, 因此需要在编译时关闭该选项,使用-fno-elide-constructors 该编译选项进行关闭编译优化。

Data GetData() {
  return Data{};
}

int main() {
  Data d = GetData();    
  return 0;
}

无优化运行结果

ctor 1
copy ctor 2
dtor 1
copy ctor 3
dtor 2
dtor 3

优化运行结果

ctor 1
dtor 1

可以看出, 在 RVO 时, 对象只被构造了一次,而未 RVO 时, 对象则发生了多次拷贝。

2.2、具名返回值优化 RVO

具名返回值优化一般发生在返回一个已经创建的对象。

Data GetData() {
  Data d;
  d.i = 10;
  return d;
}

int main() {
  Data d = GetData();    
  return 0;
}

无优化运行结果

ctor 1
copy ctor 2
dtor 1
copy ctor 3
dtor 2
dtor 3

优化运行结果

ctor 1
dtor 1

结果和不具名返回值优化一样。

三、返回值优化的特殊情况

对于存在分支的函数, 若所有分支都返回同一个具名对象, 才会开启返回值优化。

3.1、所有分支返回同一具名对象(有优化)

若分支返回全是同一具名对象, 发生返回值优化

Data GetData(int param) {
  Data d;                    
  if (param % 2 == 0) {
    d.i = 1;
    return d;
  }
  else if (param % 2 == 1) {
    d.i = 2;
    return d;
  }
  return d;
}

int main() {
  Data d = GetData(0);    
  return 0;
} 

优化运行结果

ctor 1
dtor 1

3.2、所有分支返回非同一对象(无优化)

若分支返回不全是同一具名对象, 则无返回值优化。因为返回的对象在运行时确定, 编译器无法在编译期决定。

Data GetData(int param) {
  Data d;                    
  if (param % 2 == 0) {
    d.i = 1;
    return d;
  }
  else if (param % 2 == 1) {
    Data d2;
    d2.i = 2;
    return d2;
  }
  return d;
}                              

int main() {
  Data d = GetData(0);    
  return 0;
}   

运行结果

ctor 1
copy ctor 2
dtor 1
dtor 2

3.3、函数返回结果用于赋值(无优化)

需要注意的另一种情况是, 如果调用函数时, 造成的是拷贝赋值, 而不是拷贝构造, 即使是不具名的情况, 也不会发生返回值优化 (注: 换个思路理解, 编译器不清楚赋值左侧的值从创建到赋值之间, 将处于何种状态, 或者进行何种操作, 所以不会对这种形式做返回值优化. 为避免这种情况的拷贝赋值, 可以通过移动赋值来消除)。

Data GetData(int param) {
  Data d;                    
  if (param % 2 == 0) {
    d.i = 1;
    return d;
  }
  else if (param % 2 == 1) {
    d.i = 2;
    return d;
  }
  return d;
}

int main() {
  Data d;
  d = GetData(0);    
  return 0;
} 

运行结果

ctor 1
ctor 2
copy assign 2 to 1
dtor 2
dtor 1

3.4、函数返回成员对象 (无优化)

函数返回的是局部对象的成员变量, 也无法作用返回值优化, 即使是匿名变量。

struct DataWrap {
  Data d;
};

Data GetData() {
  DataWrap dw;
  return dw.d;        
}                               

int main() {
  Data d = GetData();    
  return 0;
}  

运行结果

ctor 1
copy ctor 2
dtor 1
dtor 2

3.5、函数返回参数或者全局变量 (无优化)

函数返回的是输入参数或者全局变量, 也无返回值优化。

Data GetData(Data d) {
  return d;        
}                              

int main() {
  Data d1;                  
  Data d2 = GetData(d1);      
  return 0;
}

运行结果

ctor 1
copy ctor 2
copy ctor 3
dtor 2
dtor 3
dtor 1

3.6、由 std::move 返回 (无优化)

通过显式调用 std::move 返回函数结果往往是错误的,即使如此, 这试图使对象显式调用移动构造函数, 导致返回值优化被抑制。

Data GetData() {
  Data d;                 
  return std::move(d);                  
}                           

int main() {
  Data d = GetData();    
  return 0;
}   

运行结果

ctor 1
copy ctor 2
dtor 1
dtor 2

四、参考资料

https://juejin.cn/post/7145704295353516063

https://21xrx.com/Articles/read_article/85173

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: rvo2是一款流行的多体仿真引擎,其queryvisibility函数主要用于查询两个Agent之间是否存在可视障碍。可视障碍指的是视线被遮挡而无法直接看到对方的情况。这个函数的输入参数包括两个Agent的代号,以及它们所在位置的坐标信息等。当函数被调用时,rvo2会基于这些信息来判断两个Agent之间是否存在可视障碍。 对于具体的实现方法,RVO2是通过将两个Agent看作固定半径的圆来实现的。在判断两个Agent之间是否存在可视障碍时,RVO2会检测以两个圆为圆心、圆心距离为直径的大圆,然后依次检测该大圆上的若干个点是否被障碍物遮挡。如果存在可视障碍,则两个Agent之间的可视距离被限制为大圆上两个点之间的线段长度。 最终,queryvisibility函数将返回一个bool类型的值,用于表示两个Agent之间是否存在可视障碍。这个函数在多个应用场景中都有广泛的应用,如人群模拟、机器人导航等。在实际应用中,我们往往可以通过修改一些参数来改变这个函数的表现,以满足不同的需求。 ### 回答2: rvo2是一个用于人群模拟和路径规划的库。queryvisibility是rvo2库中的一个函数,用于查询在可行路径中是否存在障碍物。 在rvo2库中,人群的移动路径是通过导航网格(navigation mesh)来表示的。在该网格中,障碍物被定义为无法穿过的区域,并用多边形表示。 使用queryvisibility函数时,会根据给定的起点和终点,检查两者之间是否存在障碍物。如果存在障碍物,则返回false,表示两点之间不可行;如果不存在障碍物,则返回true,表示两点之间可行。 这个函数的作用在人群模拟和路径规划中非常重要。当人群移动时,需要根据当前位置和目标位置,查询路径上是否有障碍物。如果路径上存在障碍物,则需要通过其他路径避免碰撞和拥挤。 总的来说,queryvisibility是rvo2库中一个用于路径检测的重要函数,确保人群可以在避开障碍物的情况下顺利移动。 ### 回答3: rvo2 queryvisibility是一个查询函数,用于在RVO2库中计算两个移动物体之间的可见性。该函数主要用于行人和机器人等移动物体的路径规划和避障等问题。 该函数使用了远程可视化障碍(remote visibility obstacles,简称RVO)的概念,计算两个移动物体之间的RVO集合,即可视区域。如果两个移动物体之间存在可行的路径,则它们之间的RVO集合为空,否则为非空。 RVO2 queryvisibility函数的返回值为布尔类型,表示两个移动物体之间是否存在可行路径。该函数需要传入两个移动物体的位置和半径等参数,还可以传入一些可选参数,如最大查询距离和最大查询时间等。 总之,rvo2 queryvisibility是一个在RVO2库中非常重要的查询函数,它能够帮助程序员计算出两个移动物体之间的可见区域,从而实现行人和机器人等移动物体的路径规划和避障等功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值