【转】cpu cache line 原理

 
参考:
一个讲解Direct Mapped Cache很是深刻浅出的文章:
 
CPU cache
 
 
=============================================
 
整体认识
cpu的cache一般较大, 好比 128KB, 被划分为多个有固定大小的cache line, cache line一般是32Byte或64Byte.

CPU内部的cache种类, 至少有三种
1) 指令cache
2) 数据cache 一般有多级 multi-level
3) TLB 加速虚拟地址2物理地址转换


cache entry (cache条目)
包含以下部分
1) cache line : 从主存一次copy的数据大小)
2) tag : 标记cache line对应的主存的地址
3) falg : 标记当前cache line是否invalid, 若是是数据cache, 还有是否dirty


cpu访问主存的规律
1) cpu历来都不直接访问主存, 都是经过cache间接访问主存
2) 每次须要访问主存时, 遍历一遍所有cache line, 查找主存的地址是否在某个cache line中.
3) 若是cache中没有找到, 则分配一个新的cache entry, 把主存的内存copy到cache line中, 再从cache line中读取.


cache中包含的cache entry条目有限, 因此, 必须有合适的cache淘汰策略
通常使用的是LRU策略.
将一些主存区域标记为non-cacheble, 能够提升cache命中率, 下降没用的cache


回写策略
cache中的数据更新后,须要回写到主存, 回写的时机有多种
1) 每次更新都回写. write-through cache
2) 更新后不回写,标记为dirty, 仅当cache entry被evict时才回写
3) 更新后, 把cache entry送如回写队列, 待队列收集到多个entry时批量回写.


cache一致性问题
有两种状况可能致使cache中的数据过时
1) DMA, 有其余设备直接更新主存的数据
2) SMP, 同一个cache line存在多个CPU各自的cache中. 其中一个CPU对其进行了更新.


cpu stall cpu失速
指的是当cache miss时(特别是read cache miss), cpu在等待数据从内存读进去cache中期间, 没事可作.
解决此问题的方法有
1) 超线程技术. CPU在硬件层面, 把一个CPU模拟成两个CPU, 在上层看来是两个CPU. 并发的执行两个线程. 这样当一个线程因cache miss在等待时, 另外一个线程能够执行.


主存的一个地址, 须要被映射进哪一个cache line? (术语:Associativity)
根据映射策略的不一样而不一样


1) 最笨的, 一个地址可被映射进任意cache line (fully associative)
   带来的问题是, 当寻找一个地址是否已经被cache时, 须要遍历每个cache line来寻找, 这个代价不可接受.
   就像停车位能够你们随便停同样, 停的时候简单的, 找车的时候须要一个一个停车位的找了.
   你想下, cpu想知道一个地址是否已经在cache中了, 须要把所有cache line找一边, 那该有多慢?


2) Direct Mapped Cache  (至关于1-way associative)
   这个就是至关于hash了, 每一个地址能被映射到的cache line是固定的. 
   每一个人的停车位是固定分配好的. 能够直接找到.
   缺点是, 由于人多车少, 极可能几我的争用同一个车位, 致使cache 淘汰频繁. 须要频繁的从主存读取数据到cache, 这个代价也较高.
   因为cache中cache line的个数都是2的指数个. 那么, hash算法就很简单了, 不用取模, 直接把内存地址的某几个bit位拿出来便可. 好比cache line有128(2^7)个, cache line的大小是32(2^5)字节, 
   那么一个32位地址的 0~4位做为cache line内部偏移, 5~11位做为cache line的索引便可. 剩下的bit12~31做为当前cache line的tag. tag的做用时, 当有另一个地址也映射到同一个cache line时, tag用来比较两个地址是否是同一个地址. 毕竟同一个cache-line能够对应的内存的位置很是多个的.


3) 2-way associative
   是fully associative和Direct Mapped Cache的折中.
   2-way, 每个人能够有两个停车位, 这样当一个停车位被占了的时候, 还有机会寻找另一个. 虽然人数众多, 但同时来找停车位的人并很少. (至关于不少人的车在外面,没有开回来)
   因此, 2-way associative近似的至关于有了2倍大小的cache, 使用Direct Mapped Cache策略.

cache-hash.jpg
注意, 这图只统计了cache miss率, 很显然full-associative是作好的. 可是full-associative致使的判断一个地址是否在cache中的代价是很是昂贵的.因此, 生产环境通常都是2-way associative
======================================================

多线程变成中避免以及识别错误的共享变量方式 主要解决在SMP环境下cache line被频繁刷新的的问题
Avoiding and Identifying False Sharing Among Threads
 
举例:
// 以下代码在SMP环境下存在cache频繁刷新问题
double sum=0.0, sum_local[NUM_THREADS];
#pragma omp parallel num_threads(NUM_THREADS)
{
 int me = omp_get_thread_num();
 sum_local[me] = 0.0;

 #pragma omp for
 for (i = 0; i < N; i++)
 sum_local[me] += x[i] * y[i];

 #pragma omp atomic
 sum += sum_local[me];
}
由于sum_local数组是个全局变量, 多个线程都会访问, 而且, 各个线程访问的地方很接近, 会致使一个线程更新, 其余CPU的cache line失效.
smp.jpg
 
解决该问题的方法是
1) 不一样线程之间尽可能少的访问全局变量, 尽可能使用线程局部变量.
2) 若是必定要访问, 尽可能让各个线程本身访问的区域cacheline对齐.
3) 频繁更新的存储和不频繁更新的存储分开.
相关文章
相关标签/搜索