CPU高速缓存行对齐

来源:互联网 发布:整形医院网络咨询招聘 编辑:程序博客网 时间:2024/06/06 07:00

转自:http://blog.csdn.net/cbn236337758/article/details/45157685


CPU的高速缓存一般分为一级缓存和二级缓存,现今更多的CPU更是提供了三级缓存。CPU在运行时首先从一级缓存读取数据,如果读取失败则会从二级缓存读取数据,如果仍然失败则再从内存中存读取数据。而CPU从一级缓存或二级缓存或主内存中最终读取到数据所耗费的时钟周期差距是非常之大的。因此高速缓存的容量和速度直接影响到CPU的工作性能。 一级缓存都内置在CPU内部并与CPU同速运行,可以有效的提高CPU的运行效率。一级缓存越大,CPU的运行效率往往越高

一级缓存又分为数据缓存和指令缓存,他们都由高速缓存行组成,对于X86架构的CPU来说,高速缓存行一般是32个字节,早期的CPU大约只有512行高速缓存行,也就是说约16k的一级缓存。而现在的CPU一般都是32K以上的一级缓存。

当CPU需要读取一个变量时,该变量所在的以32字节分组的内存数据将被一同读入高速缓存行,所以,对于性能要求严格的程序来说,充分利用高速缓存行的优势非常重要。一次性将访问频繁的32字节数据对齐后读入高速缓存中,减少CPU高级缓存与低级缓存、内存的数据交换

  但是对于多CPU的计算机,情况却又不一样了。例如:

  1、      CPU1 读取了一个字节,以及它和它相邻的字节被读入 CPU1 的高速缓存。

  2、      CPU2 做了上面同样的工作。这样 CPU1 , CPU2 的高速缓存拥有同样的数据。

  3、      CPU1 修改了那个字节,被修改后,那个字节被放回 CPU1 的高速缓存行。但是该信息并没有被写入RAM 。

  4、      CPU2 访问该字节,但由于 CPU1 并未将数据写入 RAM ,导致了数据不同步。

  当一个 CPU 修改高速缓存行中的字节时,计算机中的其它 CPU会被通知,它们的高速缓存将视为无效。于是,在上面的情况下, CPU2 发现自己的高速缓存中数据已无效, CPU1 将立即把自己的数据写回 RAM ,然后 CPU2 重新读取该数据。 可以看出,高速缓存行在多处理器上会导致一些不利。

  从上面的情况可以看出,在设计数据结构的时候,应该尽量将只读数据与读写数据分开,并具尽量将同一时间访问的数据组合在一起。这样 CPU 能一次将需要的数据读入。

  如:

  Struct __a

  {

  Int id; // 不易变

  Int factor;// 易变

  Char name[64];// 不易变

  Int value;// 易变

  } ;

  这样的数据结构就很不利。

  在 X86 下,可以试着修改和调整它

  Struct __a

  {

  Int id; // 不易变

  Char name[64];// 不易变

  Char __Align[32 – sizeof(int)+sizeof(name)*sizeof(name[0])%32]

  Int factor;// 易变

  Int value;// 易变

  Char __Align2[32 –2* sizeof(int)%32]

  } ;

  32 – sizeof(int)+sizeof(name)*sizeof(name[0])%32

  32 表示 X86 架构缓存中,高速缓存行为 32字节 大小。 __Align 用于显式对齐。

以上背景知识对于我们编程至少有如下两个意义:

1、有些编译器会对变量进行优化,这种优化可能导致CPU对变量的读取指令始终指向高速缓存,而不是内存。这样的话,当一个变量被多个线程共享的时候,可能会导致一个线程对变量的设置始终无法在另一个线程中体现,因为另一个线程在另一个CPU上运行,并且变量的值在该CPU的高速缓存中!volatile关键字告诉编译器生成的代码始终从内存中读取变量,而不要做类似优化。

2、在多CPU环境下,合理的设置高速缓存对齐,以使得CPU之间的高速缓存同步动作尽量的少发生,以提升性能。要对齐高速缓存,首先要知道目标CPU的高速缓存行的大小,然后用__declspec(align(#))来告诉编译器为变量或结构设置指定符合高速缓存行大小的数据大小,例如:

1
2
3
4
struct CACHE_ALIGN S1 { // cache align all instances of S1
   int a, b, c, d;
};
struct S1 s1;   // s1 is 32-byte cache aligned