gem5: 实现最后一级缓存LLC分区

问题:如何实现cpu中最后一级缓存分区呢?如对于LLC 2MB,cache line 64Byte, 32-way,将其分区为16路相连并保持1024 cache sets不变呢?

比如下面的4个set, 8路相连,分区后变成4路有效(标志为1的)的cache。

11110000
11110000
11110000
11110000

思路:

  • 分区的关键部分包含lru.cc和CacheSet.hh;
  • lru.cc中关于访问accessBlock()和替换findVictim()的部分需要修改;
  • accessBlock()控制每次针对LLC的访问仅访问分区内的内容(标志为1的那部分);
  • findVictim()用于控制LLC发生替换时,替换分区内最后一个块;
  • 增加一个option用于指定分区,如–part=16选项;

方法:
1.修改configs/common/Options.py,增加 –part选项;

2.将分区选项作为缓存的一部分,类似缓存的assoc参数,修改src/mem/cache/BaseCache.py,增加 part = Param.Int("PartitionSize")

3.使part在缓存配置中生效,修改configs/common/CacheConfig.py关于一级,二级和三级缓存的设置,如下:

# 三级缓存定义中增加part=options.part,指定LLC分区大小
if options.l3cache:
        system.l3 =      l3_cache_class(clk_domain=system.cpu_clk_domain,
                                   size=options.l3_size,
                                   part=options.part,
                                   assoc=options.l3_assoc)

# 修改一级缓存的part参数,因为只分区LLC,故part等于l1i_assoc不变
if options.caches:
            icache = icache_class(size=options.l1i_size,
                                  part=options.l1i_assoc,
                                  assoc=options.l1i_assoc)
            dcache = dcache_class(size=options.l1d_size,
                                  part=options.l1d_assoc,
                                  assoc=options.l1d_assoc)

# 修改二级缓存的part参数,因为只分区LLC,故part等于l2_assoc不变
if options.l2cache:
            system.cpu[i].l2 = l2_cache_class(clk_domain=system.cpu_clk_domain,
                                          size=options.l2_size,
                                          part=options.l2_assoc,
                                          assoc=options.l2_assoc)

4.为了使part分区对缓存的访问行为产生影响,故需要修改换成替换策略。首先在src/mem/cache/tags/lru.hh中定义part,

//在assoc下面加入part定义,必须按照这个顺序,与src/mem/cache/BaseCache.py中的定义顺序需要一致。
 protected:
    /** The associativity of the cache. */
    const unsigned assoc;
    /** The partition size. by sff */
    const unsigned part;
    /** The number of sets in the cache. */
    const unsigned numSets;

其次,修改src/mem/cache/tags/lru.cc,初始化part参数。

//增加 part(p->part)的初始化
LRU::LRU(const Params *p)
    :BaseTags(p), assoc(p->assoc), part(p->part),
     numSets(p->size / (p->block_size * p->assoc))

再次,修改src/mem/cache/tags/Tags.py中关于LRU类中定义参数part,因为Params *p引用的时候需要该设置。

class LRU(BaseTags):
    type = 'LRU'
    cxx_class = 'LRU'
    cxx_header = "mem/cache/tags/lru.hh"
    assoc = Param.Int(Parent.assoc, "associativity")
    part = Param.Int(Parent.part, "partition") 

通过测试,发现cacheset中不能识别part参数,需要修改src/mem/cache/tags/cacheset.hh,增加其定义:

template <class Blktype>
class CacheSet
{
  public:
    /** The associativity of this set. */
    int assoc;
     //增加部分
    /** The partition size. */
    int part; 

通过测试,发现cacheset中识别的part参数有误,是由于src/mem/cache/tags/lru.cc中未给set[i]赋值。

//在lru的初始化中添加sets[i].part = part;
unsigned blkIndex = 0;       // index into blks array
    for (unsigned i = 0; i < numSets; ++i) {
        sets[i].assoc = assoc;
        sets[i].part = part;

测试方法备注:
使用下面的调试方法测试,注意l1, l2和l3的相连度都不同,方便调试的时候区分。

# 修改源代码src中内容后,运行前一定要编译一次,否则修改代码不会生效。
scons -j 8 build/ALPHA/gem5.debug

# 开始调试
gdb --args  build/ALPHA/gem5.debug configs/example/spec06_l3_se.py --benchmark=bzip2 -n 1 --cpu-type=detailed --cpu-clock=2GHz --caches --l1i_size=32kB --l1i_assoc=4 --l1d_size=32kB --l1d_assoc=8 --l2cache --l2_size=256kB --l2_assoc=16 --l3cache --l3_size=2MB --l3_assoc=32 --part=9

#设置断点
b lru.cc:80  #即LRU初始化代码中
b cacheset.hh:100 #即 way_id = i;中测试part

#运行到lru.cc:80时,通过下面的方式鉴别是否设置正确,cacheset也一样
p assoc
p part
p name()

# 如果断点无法调试时,可以设置--debug-flags来生成trace调试

5.修改分区的访问控制,访问会调用src/mem/cache/tags/cacheset.hh中的findBlk函数,修改它即可。对于LLC的访问将限定在part内。

template <class Blktype>
Blktype*
CacheSet<Blktype>::findBlk(Addr tag, int& way_id) const
{
    /**
     * Way_id returns the id of the way that matches the block
     * If no block is found way_id is set to assoc.
     */
    /** 
     * this is the source code.
    way_id = assoc;
    for (int i = 0; i < assoc; ++i) {
        if (blks[i]->tag == tag && blks[i]->isValid()) {
            way_id = i;
            return blks[i];
        }
    }
    */
    // modifed for LLC partition. L1 and L2 access normal.  
    way_id = part;
    for (int i = 0; i < part; ++i) {
        if (blks[i]->tag == tag && blks[i]->isValid()) {
            way_id = i;
            return blks[i];
        }
    }
    return NULL;
}

6.修改分区的替换方式。src/mem/cache/tags/lru.cc中的findVictim函数。在part分区最后替换。

LRU::BlkType*
LRU::findVictim(Addr addr, PacketList &writebacks)
{
    unsigned set = extractSet(addr);
    // grab a replacement candidate
    //BlkType *blk = sets[set].blks[assoc-1];  this is the source code.
    BlkType *blk = sets[set].blks[part-1]; // modified

    if (blk->isValid()) {

        DPRINTF(CacheRepl, "set %x: selecting blk %x for replacement, blk refCount: %d, drd: %d \n",
                set, regenerateBlkAddr(blk->tag, set), blk->refCount, blk->drd);
    }
    return blk;
}

7.重复上述备注中的测试,Done! 查看part及part分区后的数据是否如预期一样。分区修改完毕。


注意:
需要在三级缓存配置的se.py中添加如下内容,以免以后使用se.py时没有设置part参数而导致LLC自动分区了。

# default LLC(L3) partition 0, we should specify this args. Otherwise when we do not want partition, it still executes. by sff
if options.part == 0:
    print "Error: default LLC(L3) partition is 0, we should specify this args '--part=PART'. Otherwise when we do not want partition, it still executes."
    sys.exit(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值