提高C++性能的编程技术笔记:虚函数、返回值优化+测试代码

虚函数:在以下几个方面,虚函数可能会造成性能损失:构造函数必须初始化vptr(虚函数表);虚函数是通过指针间接调用的,所以必须先得到指向虚函数表的指针,然后再获得正确的函数偏移量;内联是在编译时决定的,编译器不可能把运行时才解析的虚函数设置为内联

无法内联虚函数造成的性能损失最大。

某些情况下,在编译期间解析虚函数的调用是可能的,但这是例外情况。由于在编译期间不能确定所调用的函数所属的对象类型,所以大多数虚函数调用都是在运行期间解析的。编译期间无法解析对内联造成了负面影响。由于内联是在编译期间确定的,所以它需要具体函数的信息,但如果在编译期间不能确定将调用哪个函数,就无法使用内联。

评估虚函数的性能损失就是评估无法内联该函数所造成的损失。这种损失的代价并不固定,它取决于函数的复杂程度和调用频率。一种极端情况是频繁调用的简单函数,它们是内联的最大受益者,若无法内联则会造成重大性能损失。另一极端情况是很少调用的复杂函数。

通过对类选择进行硬编码或者将它作为模板参数来传递,可以避免使用动态绑定。

因为函数调用的动态绑定是继承的结果,所以消除动态绑定的一种方法是用基于模板的设计来替代继承。模板把解析的步骤从运行期间提前到编译期间,从这个意义上说,模板提高了性能。而对于我们所关心的编译时间,适当增加也是可以接受的。

返回值优化通过转换源代码和消除对象的创建来加快源代码的执行速度,这种优化称为返回值优化(Return Value Optimization, RVO)

编译器优化要保证原来计算的正确性。然而对于RVO来说,这一点并不总是易于实现的。既然RVO不是强制执行的,编译器就不会对复杂的函数执行RVO。例如,如果函数有多个return语句返回不同名称的对象,这样就不会执行RVO。如果想使用RVO,就必须返回相同名称的对象。

当编译器无法执行RVO时,可按计算性构造函数的形式来实现。

如果必须按值返回对象,通过RVO可以省去创建和销毁局部对象的步骤,从而改善性能。

RVO的应用要遵照编译器的实现而定。这需要参考编译器文档或通过实验来判断是否使用RVO以及何时使用。

通过编写计算性构造函数可以更好地使用RVO。

以下是测试代码(return_value_optimization.cpp):

#include "return_value_optimization.hpp"
#include 
#include 

namespace return_value_optimization_ {

// reference: 《提高C++性能的编程技术》:第四章:返回值优化

class Complex {
	friend Complex operator + (const Complex&, const Complex&);
	friend void Complex_Add(const Complex&, const Complex&, Complex&);
	friend Complex Complex_Add2(const Complex&, const Complex&);
	friend Complex Complex_Add3(const Complex&, const Complex&);

public:
	Complex(double r =0.0, double i =0.0) : real(r), imag(i) {} // 默认构造函数
	Complex(const Complex& c) : real(c.real), imag(c.imag) {} // 拷贝构造函数
	Complex(const Complex& a, const Complex& b) : real(a.real + b.real), imag(a.imag + b.imag) {} // 计算性构造函数
	Complex& operator = (const Complex& c) { this->real = c.real; this->imag = c.imag; return *this; }// 赋值运算符
	~Complex() {}

private:
	double real;
	double imag;

};

Complex operator + (const Complex& a, const Complex& b)
{
	Complex retVal;
	retVal.real = a.real + b.real;
	retVal.imag = a.imag + b.imag;

	return retVal;
}

// 消除局部对象retVal,直接把返回值放到__tempResult临时对象中来实现优化,这就是返回值优化(RVO)
void Complex_Add(const Complex& a, const Complex&b, Complex& __tempResult)
{
	__tempResult.real = a.real + b.real;
	__tempResult.imag = a.imag + b.imag;
}

Complex Complex_Add2(const Complex& a, const Complex& b)
{
	Complex retVal;
	retVal.real = a.real + b.real;
	retVal.imag = a.imag + b.imag;

	return retVal;
}

// 通过计算性构造函数来执行加操作
Complex Complex_Add3(const Complex& a, const Complex& b)
{
	return Complex(a, b);
}

int test_return_value_optimization_1()
{
	// 测试两种加操作的实现性能:普通加操作、RVO加操作
	using namespace std::chrono;
	high_resolution_clock::time_point time_start, time_end;
	const int cycle_number {100000000};

{ // 普通加操作
	Complex a(1, 0);
	Complex b(2, 0);
	Complex c;

	time_start = high_resolution_clock::now();
	for (int i = 0; i < cycle_number; ++i) {
		c = a + b;
	}
	time_end = high_resolution_clock::now();
	std::cout<<"common add time spent: "<<(duration_cast>(time_end - time_start)).count()<<" seconds\n";
}

{ // RVO加操作
	Complex a(1, 0);
	Complex b(2, 0);
	Complex c;
	
	time_start = high_resolution_clock::now();
	for (int i = 0; i < cycle_number; ++i) {
		Complex_Add(a, b, c);
	}
	time_end = high_resolution_clock::now();
	std::cout<<"RVO add time spent: "<<(duration_cast>(time_end - time_start)).count()<<" seconds\n";
}

	// 测试两种加操作的实现性能:普通加操作、使用计算性构造函数

{ // 普通加操作
	Complex a(1, 0);
	Complex b(2, 0);
	Complex c;

	time_start = high_resolution_clock::now();
	for (int i = 0; i < cycle_number; ++i) {
		c = Complex_Add2(a, b);
	}
	time_end = high_resolution_clock::now();
	std::cout<<"common add time spent: "<<(duration_cast>(time_end - time_start)).count()<<" seconds\n";
}

{ // 使用计算性构造函数
	Complex a(1, 0);
	Complex b(2, 0);
	Complex c;

	time_start = high_resolution_clock::now();
	for (int i = 0; i < cycle_number; ++i) {
		c = Complex_Add3(a, b);
	}
	time_end = high_resolution_clock::now();
	std::cout<<"计算性构造函数 add time spent: "<<(duration_cast>(time_end - time_start)).count()<<" seconds\n";

}

	return 0;
}

} // namespace return_value_optimization_

运行结果如下:

GitHub: https://github.com/fengbingchun/Messy_Test  

你可能感兴趣的:(C/C++/C++11)