笔记列表
《提高C++性能的编程技术》笔记:总结
《提高C++性能的编程技术》笔记:跟踪
《提高C++性能的编程技术》笔记:构造函数、析构函数
《提高C++性能的编程技术》笔记:临时对象
《提高C++性能的编程技术》笔记:内存池(单线程、多线程)
《提高C++性能的编程技术》笔记:内联
《提高C++性能的编程技术》笔记:STL
《提高C++性能的编程技术》笔记:引用计数
《提高C++性能的编程技术》笔记:编码优化
《提高C++性能的编程技术》笔记:设计优化/可扩展性/系统体系结构
当提高性能时,我们必须记住以下几点:
(1). 内存不是无限大的。虚拟内存系统使得内存看起来是无限的,而事实上并非如此。
(2). 内存访问开销不是均衡的。对缓存、主内存和磁盘的访问开销不在同一个数量级之上。
(3). 我们的程序没有专用的CPU,只能间歇地获得一个时间片。
(4). 在一台单处理器的计算机上,并行的线程并不是真正地并行执行,它们是轮询的。
“性能”可以有几种衡量标准,最常见的两种是空间效率和时间效率。空间效率标准寻求占用最小内存的软件解决方案。相似的,时间效率标准寻求占用最少处理器周期的解决方案。时间效率通常以响应时间和吞吐量来作为衡量标准。其它衡量标准还有编译时间和可执行文件的大小。
许多C++程序员在跟踪代码时通常的做法是,定义一个简单的Trace类将诊断信息打印到日志文件中。程序员可以在每个想要跟踪的函数中定义一个Trace对象,在函数的入口和出口Trace类可以分别写一条信息。尽管Trace对象将增加程序额外的执行开销,但是它能够帮助程序员找出问题而无须使用调试器。
最理想的跟踪性能优化的方法应该能够完全消除性能开销,即把跟踪调用嵌入在#ifdef块内。使用这种方法的不足在于必须重新编译程序来打开或关闭跟踪。还有一种选择:可以通过与正在运行的程序通信来动态地控制跟踪。Trace类能够在记录任何跟踪信息之前先检查跟踪状态。
影响C++性能的因素:I/O的开销是高昂的;函数调用的开销是要考虑的一个因素,因此我们应该将短小的、频繁调用的函数内联;复制对象的开销是高昂的,最好选择传递引用,而不是传递值。
内联对大块头函数的影响是无足轻重的。只有在针对那些调用和返回开销占全部开销的绝大部分的小型函数时,内联对性能的改善才有较大的影响。内联消除了常被使用的小函数调用所产生的函数开销。完美适合内联的函数就恰好是非常不适合跟踪的。
对象定义会触发隐形地执行构造函数和析构函数。我们称其为”隐性执行”而不是”隐性开销”是因为对象的构造和销毁并不总是意味产生开销。
通过引用传递对象还是不能保证良好的性能,所以避免对象的复制的确有利于提高性能,但是如果我们不必一开始就创建和销毁该对象的话,这种处理方式将更有利于性能的提升。
在完成同样的简单工作时,char指针有时可以比string对象更有效率。
以下是测试代码(the_tracing_war_story.cpp),分别有Trace1,Trace2,Trace3三个简单的类,它们的性能逐渐提高:
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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
| #include <string> #include <iostream> #include <chrono> namespace tracing_war_story_ {
class Trace1 { public: Trace1(const std::string& name); ~Trace1(); void debug(const std::string& msg); static bool traceIsActive; private: std::string theFunctionName; }; inline Trace1::Trace1(const std::string& name) : theFunctionName(name) { if (traceIsActive) { std::cout << "Enter function: " << name << std::endl; } } inline Trace1::~Trace1() { if (traceIsActive) { std::cout <<"Exit function: " << theFunctionName << std::endl; } } inline void Trace1::debug(const std::string& msg) { if (traceIsActive) { std::cout << msg << std::endl; } } bool Trace1::traceIsActive = false; int addOne0(int x) { return x+1; } int addOne1(int x) { std::string name = "addOne1"; Trace1 t(name); return x+1; }
class Trace2 { public: Trace2(const char* name); ~Trace2(); void debug(const char* msg); static bool traceIsActive; private: std::string theFunctionName; }; inline Trace2::Trace2(const char* name) : theFunctionName(name) { if (traceIsActive) { std::cout << "Enter function: " << name << std::endl; } } inline Trace2::~Trace2() { if (traceIsActive) { std::cout <<"Exit function: " << theFunctionName << std::endl; } } inline void Trace2::debug(const char* msg) { if (traceIsActive) { std::cout << msg << std::endl; } } bool Trace2::traceIsActive = false; int addOne2(int x) { char* name = (char*)"addOne2"; Trace2 t(name); return x+1; }
class Trace3 { public: Trace3(const char* name); ~Trace3(); void debug(const char* msg); static bool traceIsActive; private: std::string* theFunctionName; }; inline Trace3::Trace3(const char* name) : theFunctionName(nullptr) { if (traceIsActive) { std::cout << "Enter function: " << name << std::endl; theFunctionName = new std::string(name); } } inline Trace3::~Trace3() { if (traceIsActive) { std::cout <<"Exit function: " << theFunctionName << std::endl; delete theFunctionName; } } inline void Trace3::debug(const char* msg) { if (traceIsActive) { std::cout << msg << std::endl; } } bool Trace3::traceIsActive = false; int addOne3(int x) { char* name = (char*)"addOne3"; Trace3 t(name); return x+1; } }
using namespace tracing_war_story_; int main() { Trace1::traceIsActive = false;
std::string name = "test_tracing_war_story"; Trace1 t(name); std::string moreInfo = "more interesting info"; t.debug(moreInfo); using namespace std::chrono; high_resolution_clock::time_point timeStart, timeEnd; int count = 1000000; timeStart = high_resolution_clock::now(); for (int i = 0; i < count; ++i) { addOne0(i); } timeEnd = high_resolution_clock::now(); std::cout<< "addOne0 time spend: " <<(duration_cast<duration<double>>(timeEnd-timeStart)).count()<<" seconds"<<std::endl; timeStart = high_resolution_clock::now(); for (int i = 0; i < count; ++i) { addOne1(i); } timeEnd = high_resolution_clock::now(); std::cout<< "addOne1 time spend: " <<(duration_cast<duration<double>>(timeEnd-timeStart)).count()<<" seconds"<<std::endl; Trace2::traceIsActive = false; timeStart = high_resolution_clock::now(); for (int i = 0; i < count; ++i) { addOne2(i); } timeEnd = high_resolution_clock::now(); std::cout<< "addOne2 time spend: " <<(duration_cast<duration<double>>(timeEnd-timeStart)).count()<<" seconds"<<std::endl; Trace3::traceIsActive = false; timeStart = high_resolution_clock::now(); for (int i = 0; i < count; ++i) { addOne3(i); } timeEnd = high_resolution_clock::now(); std::cout<< "addOne3 time spend: " <<(duration_cast<duration<double>>(timeEnd-timeStart)).count()<<" seconds"<<std::endl; return 0; }
|
利用 godbolt 执行,相应代码见 点击这里 测试结果如下:
1 2 3 4
| addOne0 time spend: 1.3e-07 seconds addOne1 time spend: 0.00747676 seconds addOne2 time spend: 0.00565086 seconds addOne3 time spend: 0.003478 seconds
|