问题:如何实现cpu中最后一级缓存分区呢?如对于LLC 2MB,cache line 64Byte, 32-way,将其分区为16路相连并保持1024 cache sets不变呢?
比如下面的4个set, 8路相连,分区后变成4路有效(标志为1的)的cache。
1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
思路:
- 分区的关键部分包含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()
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 16, we should specify this args. Otherwise when we do not want partition, it still executes. by sff
if ‘--part‘ not in sys.argv:
print "Error: default LLC(L3) partition is 16, we should specify this args ‘--part=PART‘. Otherwise when we do not want partition, it still executes."
sys.exit(1)
版权声明:本文为博主原创文章,未经博主允许不得转载。