Memory Performance Attacks
问题的产生
在多核的机器上,两个各自在不同核上跑的程序,理论上,运行的速度应该跟它们在各自在单核机器上跑的速度差不多。然而现实情况却是程序的运行速度会大幅度下降,性能可能会下降好几倍。
背景
目前计算机中的CPU内存结构如上图所示,在多个CPU的机器上,不同的CPU core各自拥有独立的L1、L2 cache,但是内存却是共享的,而共享意味着冲突,意味着抢占。
上图是内存系统的一个架构图,内存是以一个一block个block的形式,组织成一个矩阵的结构,称之为bank。block的大小跟CPU的位数有关系(对于32位的机器,一个block是32bytes)。当我们想要获取某一个block的信息时,例如[row, col]这个block上的数据时,是先通过Row Address Decoder获取第row行的数据,放在一个叫ROW BUFFER的地方,然后再Column Decoder获取第row行第col列的数据。
上面说过,共享意味着冲突,意味着抢占,那么当不同的CPU core同时有获取内存中数据的需求时,会发生什么事情?这就是DRAM Controller做的事情。当有获取内存数据的请求来的时候,请求先进入一个缓冲队列REQUEST BUFFER,然后Memory Access Scheduler就会根据调度算法将这些请求调度,决定哪些请求可以优先服务。
调度算法
Memory Access Scheduler的调度算法是这样的:每一个Memory Request都会有一个是时间,表示待在缓冲区里有多长时间了。调度算法首先会优先服务那些待在缓冲区中的时间超过一定阈值的请求;如果不存在这样的请求,Scheduler就会优先服务那些有high row-buffer locality的请求。
备注:high row-buffer locality是指那些请求内存中同一个row的请求。
结果
上述的调度算法会导致Memory Access Scheduler优先服务一个拥有high row-buffer locality的程序,而增加一个访问内存没有规律的程序的内存访问时间,导致后者性能急剧下降。
解决办法
论文中关于解决办法那一节有太多的数学公式,所以也没看得很仔细,囧。大概的思路是将访存时间分为以下几个:
- 请求到达REQUEST BUFFER的时间t1
- 以及将block中的数据传输到L2 cache的完成时间t2。
对于1,我们是无法改变的,只能对2进行改进。论文中解决办法的核心思想就是将进程的平均等待时间(t2 - t1)都是差不多相等的。当各个进程的平均等待时间差不多的时候,就使用FR-FCFS算法(就是上面所说的算法);当各个进程的平均等待时间相差较大的时候,就提高平均等待时间较大的进程的访存优先级,优先服务这些进程的访存请求。
其它
论文中证实了它给出的解决办法在实际中也是可行的。