基于《计算机体系结构:量化研究方法》1.9 计算机设计的量化原理
了解更多的信息后,会发现相同的思路和原则贯穿底层到上层,硬件到软件。这一章节中的设计指导原则对于上层编程也是有参考意义的。
充分利用并行
充分利用并行是提升性能的最重要的方法之一。从最底层的CPU到操作系统级,再到设备级,都可以通过实现并行来满足性能需求,但并行化可能会增加通信成本。
指令级并行
在单独处理器级别,充分利用指令间的并行对于实现高性能非常关键。我们可以知道并非所有指令执行都取决于与其相邻的前一条指令,因此有可能完全并行或部分并行地执行这些指令。
流水线是大家最熟悉的指令级并行示例。流水线背后的基本思想就是将指令执行重叠起来,以缩短完成指令序列的总时间。
执行下面的代码会发现执行Loop 1的时间大概是执行Loop 2的1.5倍左右。
int main()
{
int count = 100000000;
int a[2] = {0};
// Loop 1
clock_t s = clock();
for( int i = 0; i < count; ++i ){
a[0]++;
a[0]++; // 依赖于上一行代码的执行结果
}
clock_t e = clock();
printf( "Loop 1 cost %u\n", e - s );
// Loop 2
s = clock();
for( int i = 0; i < count; ++i ){
a[0]++;
a[1]++; // 完全独立于上一行代码
}
e = clock();
printf( "Loop 2 cost %u\n", e - s );
return 0;
}
线程级并行
线程可以分为硬线程和软线程
- 硬线程并行就是多个CPU并行处理
- 软线程并行就是操作系统支持多任务并行,充分利用CPU。
局部性原理
局部性分为时间局部性和空间局部性
- 时间局部性:最近访问的数据在接下去的一段时间内有很大概率还会被访问
- 空间局部性:被访问的数据附近的数据有很大概率会被访问
CPU的L1 cache、操作系统的page cache等都是基于该原理的应用。正因为存在局部性,所以可以提前加载数据到缓存或者将数据保存在缓存中以便后续快速访问。
不同数据类型的局部性是存在差异的,因此L1 cache还分为指令cache和数据cache。这对编码中使用缓存是有指导意义的,即不同类型的数据不公用缓存,可以提升各自的命中率,提高性能。
重点关注常见情形
进行设计权衡时,常见情形要优先于非常见情形。优化这些常见情形对性能的提升更明显。有一种基本定律可以量化这一原则,即Amdahl定律。利用Amdahl定律,可以计算出改善系统中某一部分能获得的性能增益。
简单粗暴的理解就是常见情形在整个执行过程中占据的比例是最大的,优化常见情形的执行时间,性价比最高。