笔记列表
《提高C++性能的编程技术》笔记:总结
《提高C++性能的编程技术》笔记:跟踪
《提高C++性能的编程技术》笔记:构造函数、析构函数
《提高C++性能的编程技术》笔记:临时对象
《提高C++性能的编程技术》笔记:内存池(单线程、多线程)
《提高C++性能的编程技术》笔记:内联
《提高C++性能的编程技术》笔记:STL
《提高C++性能的编程技术》笔记:引用计数
《提高C++性能的编程技术》笔记:编码优化
《提高C++性能的编程技术》笔记:设计优化/可扩展性/系统体系结构
虚函数:在以下几个方面,虚函数可能会造成性能损失:构造函数必须初始化vptr(虚函数表);虚函数是通过指针间接调用的,所以必须先得到指向虚函数表的指针,然后再获得正确的函数偏移量;内联是在编译时决定的,编译器不可能把运行时才解析的虚函数设置为内联。
无法内联虚函数造成的性能损失最大。
某些情况下,在编译期间解析虚函数的调用是可能的,但这是例外情况。由于在编译期间不能确定所调用的函数所属的对象类型,所以大多数虚函数调用都是在运行期间解析的。编译期间无法解析对内联造成了负面影响。由于内联是在编译期间确定的,所以它需要具体函数的信息,但如果在编译期间不能确定将调用哪个函数,就无法使用内联。
评估虚函数的性能损失就是评估无法内联该函数所造成的损失。这种损失的代价并不固定,它取决于函数的复杂程度和调用频率。一种极端情况是频繁调用的简单函数,它们是内联的最大受益者,若无法内联则会造成重大性能损失。另一极端情况是很少调用的复杂函数。
通过对类选择进行硬编码或者将它作为模板参数来传递,可以避免使用动态绑定。
因为函数调用的动态绑定是继承的结果,所以消除动态绑定的一种方法是用基于模板的设计来替代继承。模板把解析的步骤从运行期间提前到编译期间,从这个意义上说,模板提高了性能。而对于我们所关心的编译时间,适当增加也是可以接受的。
返回值优化:通过转换源代码和消除对象的创建来加快源代码的执行速度,这种优化称为返回值优化(Return Value Optimization, RVO)。
编译器优化要保证原来计算的正确性。然而对于RVO来说,这一点并不总是易于实现的。既然RVO不是强制执行的,编译器就不会对复杂的函数执行RVO。例如,如果函数有多个return语句返回不同名称的对象,这样就不会执行RVO。如果想使用RVO,就必须返回相同名称的对象。
当编译器无法执行RVO时,可按计算性构造函数的形式来实现。
如果必须按值返回对象,通过RVO可以省去创建和销毁局部对象的步骤,从而改善性能。
RVO的应用要遵照编译器的实现而定。这需要参考编译器文档或通过实验来判断是否使用RVO以及何时使用。
通过编写计算性构造函数可以更好地使用RVO。
以下是测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 #include <iostream> #include <chrono> namespace return_value_optimization_ { 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; } 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); } } using namespace return_value_optimization_;int main () { 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 spend: " <<(duration_cast<duration<double >>(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) { Complex_Add (a, b, c); } time_end = high_resolution_clock::now (); std::cout<<"RVO add time spend: " <<(duration_cast<duration<double >>(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 spend: " <<(duration_cast<duration<double >>(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 spend: " <<(duration_cast<duration<double >>(time_end - time_start)).count ()<<" seconds\n" ; } return 0 ; }
利用 godbolt 执行,相应代码见 点击这里 测试结果如下:
1 2 3 4 common add time spend: 1.67e-07 seconds RVO add time spend: 5.6e-08 seconds common add time spend: 3.3e-08 seconds 计算性构造函数 add time spend: 3.5e-08 seconds