4.2.1 高速缓存
尽管CPU的运算能力大概每年提高两倍,内存的速度每年却仅能提高10%-15%。其结果是内存操作成为了当今计算机性能损耗的元凶(译注:原文为:Thus, memory operations impose a big performance penalty on today's computers)。
作为这种发展趋势的结果,容量小的SRAM高速缓存应运而生了,这种高速缓存要比内存快的多。根据程序中引用的时空局部性,即使一个很小的高速缓存,命中率(hit ratio)也可以达到90%甚至更高。SMP系统上,每一个处理器都有自己的高速缓存。它有一个很大的优势:高速缓存命中就不需要使用内存总线(译注:原文写做common memory bus,不晓得这是何方神圣)来加载(指令或数据)了,但是它也引入了缓存一致性的问题。
当CPU访问内存中的一个字时,它首先在CPU的本地高速缓存上查找。如果找不到,内存中包含这个字的一行就会拷贝到高速缓存中。这就是高速缓存缺失(cache miss),为了提高高速缓存命中,强烈建议将物理内存中的数据按照高速缓存行(译注:以下简称缓存行)的大小进行对齐。接下来读该内存地址就会导致高速缓存命中。但是向高速缓存内写入一个字呢?这依赖于写入策略(write policy)。
写直通策略(write through)意味着每次写访问都会将缓存行写回到内存。这保证了高速缓存和内存的一致性,如果其他CPU的高速缓存监听到总线上的写访问的话,也需要在所有高速缓存间保证一致性(译注:由高速缓存自身来保证和其他高速缓存的一致性?这种机制值得商榷),但是这也是最慢的一种方法,因为每次对告诉缓存行的写访问都需要访问内存。
写回策略(write back)就没有这么复杂了。对于写访问,数据不会马上写回到内存,而是对缓存行设置一个修改位。当带有修改位的缓存行被替换掉时,它的内容才会被写入内存,接下来的写操作都会高速缓存命中。
在SMP系统中,同一块物理内存信息可能位于多个高速缓存中。SMP体系架构需要一种机制来保证多个高速缓存间的一致性。如果两个CPU从它们的高速缓存中读取同一内存字(memory word)的话,这可以工作的很好,并且,两个读操作可以同时执行。但是,如果两个CPU想要同时向它们的高速缓存写同一内存字的话,两个高速缓存中都会包含一个带有修改标志的缓存行。因此,会存在两个相同的缓存行(译注:同一个内存字在两个高速缓存中有两个不同的副本)。为了防止发生这样的事情,当一个CPU尝试修改一个缓存行时需要获取独占权(译注:原文:To prevent this, a CPU trying to modify a cache line has to get the "exclusive" right on it)。这样,其他的高速缓存对应的缓存行就会标为无效(译注:即内存字在其他高速缓存中的副本被标为无效)。其他的CPU尝试修改缓存行时需要等待之前的CPU放弃独占权,然后从之前的CPU缓存中重新读取该缓存行(译注:可以看出,这种设计使用了延迟写技术)。
让我们看看列表3中的简单自旋锁的影响。假设该锁被CPU1持有,CPU2和CPU3在该锁上等待:
test_and_set操作在每个自旋周期都会设置锁变量的值。当CPU1处于临界区时,CPU2和CPU3不停的读、写包含锁变量的缓存行。该缓存行不停的从一个高速缓存同步到其他的高速缓存(译注:因为test_and_set操作会将变量置1,这个操作会导致大量的高速缓存同步),因为CPU每次test_and_set操作都会获得一个该缓存行的副本。这被称作高速缓存行反弹(cache line bouncing),它增加了内存总线的负载。如果锁和它保护的数据处于同一缓存行的话,性能就会更差(译注:在这种情况下,获取锁的CPU在修改数据时也会导致其他CPU的高速缓存无效)。
然而,我们可以修改自旋锁的实现来适应高速缓存的这种特点(译注:减少高速缓存行反弹)。
当锁被另一个处理器持有时,原子的读-修改-写(read-modify-write)操作本身并不会获取锁。因此在锁释放前它不是一个必须的操作。相反,其他的处理器可以简单的读取目前锁的状态,并且仅仅在锁被释放时才使用一次(test_and_set)操作来尝试获取一个已被占用的锁。列表4给出了一个使用这种技术的另一种锁函数的实现。
这样,在进入内层循环前尝试获取一次锁,然后等待直到锁被释放。如果锁再次被另一个CPU持有,CPU就又会在内层循环自旋等待了。CPU在内层循环上自旋时不会独占高速缓存行(译注:也就不会修改缓存行并与其他CPU的缓存行同步)。当CPU1释放锁时,它独占该缓存行并将锁变量置0。其他CPU就可以将该锁变量重新读入缓存行并尝试获取了。
尽管如此,自旋锁仍然是非常耗时的,因为它至少要高速缓存与其他高速缓存或与内存间进行一次高速缓存行同步(cache line transfer)。
阅读(3859) | 评论(2) | 转发(0) |