CPU 为了更快的执行代码。于是当从内存中读取数据时,并不是只读自己想要的部分。而是读取足够的字节来填入高速缓存行。根据不同的 CPU ,高速缓存行大小不同。如 X86 是 32BYTES ,而 ALPHA 是 64BYTES 。并且始终在第 32 个字节或第 64 个字节处对齐。这样,当 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 中,高速缓存行为 32BYTES 大小。 __Align 用于显式对齐。