语句并非按顺序执行
C++ 程序表现得仿佛它们是按顺序执行的,完全遵守了 C++ 流程控制语句的控制。上句话影响优化的计算机行为 | 19
中的含糊其辞的“仿佛”正是许多编译器进行优化的基础,也是现代计算机硬件的许多技
巧的基础。
当然,在底层,编译器能够而且有时也确实会对语句进行重新排序以改善性能。但是编译
器知道在测试一个变量或是将其赋值给另外一个变量之前,必须先确定它包含了所有的最
新计算结果。现代处理器也可能会选择乱序执行指令,不过它们包含了可以确保在随后读
取同一个内存地址之前,一定会先向该地址写入值的逻辑。甚至微处理器的内存控制逻辑
可能会选择延迟写入内存以优化内存总线的使用。但是内存控制器知道哪次写值正在从执
行单元穿越高速缓存飞往主内存的“航班”中,而且确保如果随后读取同一个地址时会使
用这个“航班”中的值。
并发会让情况变得复杂。C++ 程序在编译时不知道是否会有其他线程并发运行。C++ 编译
器不知道哪个变量——如果有的话——会在线程间共享。当程序中包含共享数据的并发线
程时,编译器对语句的重排序和延迟写入主内存会导致计算结果与按顺序执行语句的计算
结果不同。开发人员必须向多线程程序中显式地加入同步代码来确保可预测的行为的一致
性。当并发线程共享数据时,同步代码降低了并发量。
总结:
• 在处理器中,访问内存的性能开销远比其他操作的性能开销大。
• 非对齐访问所需的时间是所有字节都在同一个字中时的两倍。
• 访问频繁使用的内存地址的速度比访问非频繁使用的内存地址的速度快。
• 访问相邻地址的内存的速度比访问互相远隔的地址的内存快。
• 由于高速缓存的存在,一个函数运行于整个程序的上下文中时的执行速度可能比运行于
测试套件中时更慢。
• 访问线程间共享的数据比访问非共享的数据要慢很多。
• 计算比做决定快。
• 每个程序都会与其他程序竞争计算机资源。
• 如果一个程序必须在启动时执行或是在负载高峰期时执行,那么在测量性能时必须加载
负载。
• 每一次赋值、函数参数的初始化和函数返回值都会调用一次构造函数,这个函数可能隐
藏了大量的未知代码。
• 有些语句隐藏了大量的计算。从语句的外表上看不出语句的性能开销会有多大。
• 当并发线程共享数据时,同步代码降低了并发量。