笔记列表

《提高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_ {

// 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);
}

} // namespace return_value_optimization_

using namespace return_value_optimization_;

int main()
{
// 测试两种加操作的实现性能:普通加操作、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 spend: "<<(duration_cast<duration<double>>(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 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