笔记列表
《提高C++性能的编程技术》笔记:总结
《提高C++性能的编程技术》笔记:跟踪
《提高C++性能的编程技术》笔记:构造函数、析构函数
《提高C++性能的编程技术》笔记:临时对象
《提高C++性能的编程技术》笔记:内存池(单线程、多线程)
《提高C++性能的编程技术》笔记:内联
《提高C++性能的编程技术》笔记:STL
《提高C++性能的编程技术》笔记:引用计数
《提高C++性能的编程技术》笔记:编码优化
《提高C++性能的编程技术》笔记:设计优化/可扩展性/系统体系结构
对象的创建和销毁往往会造成性能的损失。在继承层次中,对象的创建将引起其先辈的创建。对象的销毁也是如此。其次,对象相关的开销与对象本身的派生链的长度和复杂性相关。所创建的对象(以及其后销毁的对象)的数量与派生的复杂度成正比。
并不是说继承根本上就是代码性能的绊脚石。我们必须区分全部计算开销、必须开销和计算损失(computional penalty). 全部计算开销是一次计算中所执行的全部指令的总和。必须开销是全部指令的子集,它的结果是必要的。这部分计算是必需的,其余部分即为计算损失。计算损失是可以通过别的设计和实现来消除的那部分计算。
我们不能断言采用了复杂的继承的设计一定是坏的,也不能断定它们总是带来性能损失。我们只能说总的开销会随着派生树规模的增长而增加。如果所有的计算都是有价值的,那么它们都是必须的开销。实际上,继承层次不见得是完善的,在这种情况下,它们很可能会导致计算损失。
对象的复合与继承一样,都引入了与对象创建和销毁有关的类似性能问题。在对象被创建(或销毁)时,必须同时创建(或销毁)它所包含的成员对象。
创建和销毁被包含对象是另一个值得注意的问题:在创建(或销毁)被包含对象时无法阻止子对象的创建(或销毁),因为这是编译器自动强加的步骤。
性能优化经常需要牺牲一些其它软件目标,诸如灵活性、可维护性、成本和重用之类的重要目标经常必须为性能让步。
在C++中,不自觉地在程序开始处预先定义所有对象的做法是一种浪费。因为这样可能会创建一些直到最后都没有用到的对象。在C++中,把变量的创建延迟到第一次使用前。
构造函数和析构函数可以像手工编写的C代码一样有效。然而在实践中,它们经常包含冗余计算。
对象的创建(或销毁)触发对父对象和成员对象的递归创建(或销毁)。
要确保所编写的代码实际使用了所有创建的对象和这些对象所执行的计算。
对象的生命周期不是无偿的。至少对象的创建和销毁会消耗CPU周期。不要随意创建一个对象,除非你打算使用它。通常情况下,要等到需要使用对象的地方再创建它。
编译器必须初始化被包含的成员对象之后再执行构造函数体。你必须在初始化阶段完成成员对象的创建。这可以降低随后在构造函数部分调用赋值操作符的开销。在某些情况下,这样也可以避免临时对象的产生。
以下是测试代码:
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 #include <iostream> #include <string> #include <mutex> #include <chrono> namespace constructors_destructors_ { class SimpleMutex { public : SimpleMutex (std::mutex& mtx) : mymtx (mtx) { acquire (); } ~SimpleMutex () { release (); } private : void acquire () { mymtx.lock (); } void release () { mymtx.unlock (); } std::mutex& mymtx; }; class BaseMutex { public : BaseMutex (std::mutex& mtx) {} virtual ~BaseMutex () {} }; class DerivedMutex : public BaseMutex {public : DerivedMutex (std::mutex& mtx) : BaseMutex (mtx), mymtx (mtx) { acquire (); } ~DerivedMutex () { release (); } private : void acquire () { mymtx.lock (); } void release () { mymtx.unlock (); } std::mutex& mymtx; }; class Person1 {public : Person1 (const char * s) { name = s; } private : std::string name; }; class Person2 {public : Person2 (const char * s) : name (s) {} private : std::string name; }; } using namespace constructors_destructors_;int main () { using namespace std::chrono; high_resolution_clock::time_point time_start, time_end; const int cycle_number {100000000 }; int shared_counter {0 }; { std::mutex mtx; shared_counter = 0 ; time_start = high_resolution_clock::now (); for (int i = 0 ; i < cycle_number; ++i) { mtx.lock (); ++shared_counter; mtx.unlock (); } time_end = high_resolution_clock::now (); std::cout<< "time spend1: " <<(duration_cast<duration<double >>(time_end - time_start)).count ()<< " seconds\n" ; } { std::mutex mtx; shared_counter = 0 ; time_start = high_resolution_clock::now (); for (int i = 0 ; i < cycle_number; ++i) { SimpleMutex m (mtx) ; ++shared_counter; } time_end = high_resolution_clock::now (); std::cout<< "time spend2: " <<(duration_cast<duration<double >>(time_end - time_start)).count ()<<" seconds\n" ; } { std::mutex mtx; shared_counter = 0 ; time_start = high_resolution_clock::now (); for (int i = 0 ; i < cycle_number; ++i) { DerivedMutex m (mtx) ; ++shared_counter; } time_end = high_resolution_clock::now (); std::cout<< "time spend3: " <<(duration_cast<duration<double >>(time_end - time_start)).count ()<<" seconds\n" ; } { time_start = high_resolution_clock::now (); for (int i = 0 ; i < cycle_number; ++i) { Person1 p ("Pele" ) ; } time_end = high_resolution_clock::now (); std::cout<< "隐式初始化, time spend: " <<(duration_cast<duration<double >>(time_end - time_start)).count ()<<" seconds\n" ; } { time_start = high_resolution_clock::now (); for (int i = 0 ; i < cycle_number; ++i) { Person2 p ("Pele" ) ; } time_end = high_resolution_clock::now (); std::cout<<"显示初始化, time spend: " <<(duration_cast<duration<double >>(time_end - time_start)).count ()<<" seconds\n" ; } return 0 ; }
利用 godbolt 执行,相应代码见 点击这里 测试结果如下:
1 2 3 4 5 time spend1: 0.0660878 seconds time spend2: 0.0989312 seconds time spend3: 0.0757665 seconds 隐式初始化, time spend: 1.88212 seconds 显示初始化, time spend: 3e-08 seconds