From d70535f21078a9e2f92529faac594435a4155fab Mon Sep 17 00:00:00 2001 From: Andreas Abel Date: Mon, 30 Dec 2019 22:34:38 +0100 Subject: [PATCH] support for overriding cache sets --- tools/CacheAnalyzer/cacheLib.py | 80 +++++++++++++++++++++++-------- tools/CacheAnalyzer/cacheSeq.py | 3 +- tools/CacheAnalyzer/cacheSim.py | 51 +++++++++++++++----- tools/CacheAnalyzer/hitMiss.py | 4 +- tools/CacheAnalyzer/replPolicy.py | 24 +++++----- 5 files changed, 117 insertions(+), 45 deletions(-) diff --git a/tools/CacheAnalyzer/cacheLib.py b/tools/CacheAnalyzer/cacheLib.py index 5dfc5e9..c59f116 100755 --- a/tools/CacheAnalyzer/cacheLib.py +++ b/tools/CacheAnalyzer/cacheLib.py @@ -383,9 +383,16 @@ def getAddresses(level, wayID, cacheSetList, cBox=1, clearHL=True): raise ValueError('invalid level') -# removes ?s and !s +# removes ?s and !s, and returns the part before the first '_' def getBlockName(blockStr): - return re.sub('[?!]', '', blockStr) + return re.sub('[?!]', '', blockStr.split('_')[0]) + + +# removes ?s and !s, and returns the part after the last '_' (as int); returns None if there is no '_' +def getBlockSet(blockStr): + if not '_' in blockStr: + return None + return int(re.match('\d+', blockStr.split('_')[-1]).group()) def parseCacheSetsStr(level, clearHL, cacheSetsStr): @@ -399,7 +406,7 @@ def parseCacheSetsStr(level, clearHL, cacheSetsStr): cacheSetList.append(int(s)) else: nSets = getCacheInfo(level).nSets - if level > 1 and clearHL: + if level > 1 and clearHL and not (level == 3 and getCacheInfo(3).nSlices is not None): nHLSets = getCacheInfo(level-1).nSets cacheSetList = range(nHLSets, nSets) else: @@ -407,19 +414,34 @@ def parseCacheSetsStr(level, clearHL, cacheSetsStr): return cacheSetList +def findCacheSetForCode(cacheSetList, level): + nSets = getCacheInfo(level).nSets + sortedCacheSetList = sorted(cacheSetList) + sortedCacheSetList += [sortedCacheSetList[0] + nSets] + + maxDist = 1 + bestSet = 0 + for i in range(len(sortedCacheSetList)-1): + dist = sortedCacheSetList[i+1] - sortedCacheSetList[i] + if dist > maxDist: + maxDist = dist + bestSet = (sortedCacheSetList[i] + 1) % nSets + + return bestSet + + +def getAllUsedCacheSets(cacheSetList, seq, initSeq=''): + cacheSetOverrideList = [s for s in set(map(getBlockSet, initSeq.split()+seq.split())) if s is not None] + return sorted(set(cacheSetList + cacheSetOverrideList)) + AddressList = namedtuple('AddressList', 'addresses exclude flush wbinvd') -# cacheSets=None means do access in all sets -# in this case, the first nL1Sets many sets of L2 will be reserved for clearing L1 -# if wbinvd is set, wbinvd will be called before initSeq -def runCacheExperiment(level, seq, initSeq='', cacheSets=None, cBox=1, clearHL=True, loop=1, wbinvd=False, nMeasurements=10, warmUpCount=1, agg='avg'): - lineSize = getCacheInfo(1).lineSize - - cacheSetList = parseCacheSetsStr(level, clearHL, cacheSets) +def getCodeForCacheExperiment(level, seq, initSeq, cacheSetList, cBox, clearHL, wbinvd): + allUsedSets = getAllUsedCacheSets(cacheSetList, seq, initSeq) clearHLAddrList = None if (clearHL and level > 1): - clearHLAddrList = AddressList(getClearHLAddresses(level, cacheSetList, cBox), True, False, False) + clearHLAddrList = AddressList(getClearHLAddresses(level, allUsedSets, cBox), True, False, False) initAddressLists = [] seqAddressLists = [] @@ -432,29 +454,49 @@ def runCacheExperiment(level, seq, initSeq='', cacheSets=None, cBox=1, clearHL=T addrLists.append(AddressList([], True, False, True)) continue + overrideSet = getBlockSet(seqEl) + wayID = nameToID.setdefault(name, len(nameToID)) exclude = not '?' in seqEl flush = '!' in seqEl - addresses = getAddresses(level, wayID, cacheSetList, cBox=cBox, clearHL=clearHL) + s = [overrideSet] if overrideSet is not None else cacheSetList + addresses = getAddresses(level, wayID, s, cBox=cBox, clearHL=clearHL) if clearHLAddrList is not None and not flush: addrLists.append(clearHLAddrList) addrLists.append(AddressList(addresses, exclude, flush, False)) - ec = getCodeForAddressLists(seqAddressLists, initAddressLists, wbinvd) - log.debug('\nInitAddresses: ' + str(initAddressLists)) log.debug('\nSeqAddresses: ' + str(seqAddressLists)) + + return getCodeForAddressLists(seqAddressLists, initAddressLists, wbinvd) + + +def runCacheExperimentCode(code, initCode, oneTimeInitCode, loop, warmUpCount, codeOffset, nMeasurements, agg): + resetNanoBench() + setNanoBenchParameters(config=getDefaultCacheConfig(), msrConfig=getDefaultCacheMSRConfig(), nMeasurements=nMeasurements, unrollCount=1, loopCount=loop, + warmUpCount=warmUpCount, aggregateFunction=agg, basicMode=True, noMem=True, codeOffset=codeOffset, verbose=None) + return runNanoBench(code=code, init=initCode, oneTimeInit=oneTimeInitCode) + + +# cacheSets=None means do access in all sets +# in this case, the first nL1Sets many sets of L2 will be reserved for clearing L1 +# if wbinvd is set, wbinvd will be called before initSeq +def runCacheExperiment(level, seq, initSeq='', cacheSets=None, cBox=1, clearHL=True, loop=1, wbinvd=False, nMeasurements=10, warmUpCount=1, codeSet=None, + agg='avg'): + cacheSetList = parseCacheSetsStr(level, clearHL, cacheSets) + ec = getCodeForCacheExperiment(level, seq, initSeq=initSeq, cacheSetList=cacheSetList, cBox=cBox, clearHL=clearHL, wbinvd=wbinvd) + log.debug('\nOneTimeInit: ' + ec.oneTimeInit) log.debug('\nInit: ' + ec.init) log.debug('\nCode: ' + ec.code) - resetNanoBench() - setNanoBenchParameters(config=getDefaultCacheConfig(), msrConfig=getDefaultCacheMSRConfig(), nMeasurements=nMeasurements, unrollCount=1, loopCount=loop, - warmUpCount=warmUpCount, aggregateFunction=agg, basicMode=True, noMem=True, verbose=None) + lineSize = getCacheInfo(1).lineSize + allUsedSets = getAllUsedCacheSets(cacheSetList, seq, initSeq) + codeOffset = lineSize * (codeSet if codeSet is not None else findCacheSetForCode(allUsedSets, level)) - return runNanoBench(code=ec.code, init=ec.init, oneTimeInit=ec.oneTimeInit) + return runCacheExperimentCode(ec.code, ec.init, ec.oneTimeInit, loop, warmUpCount, codeOffset, nMeasurements, agg) def printNB(nb_result): @@ -603,7 +645,7 @@ def getAgesOfBlocks(blocks, level, seq, initSeq='', maxAge=None, cacheSets=None, newBlocks = getUnusedBlockNames(nNewBlocks, seq+initSeq, 'N') curSeq += ' '.join(newBlocks) + ' ' + block + '?' - nb = runCacheExperiment(level, curSeq, initSeq=initSeq, cacheSets=cacheSets, cBox=cBox, clearHL=clearHL, loop=0, wbinvd=wbinvd, nMeasurements=nMeasurements) + nb = runCacheExperiment(level, curSeq, initSeq=initSeq, cacheSets=cacheSets, cBox=cBox, clearHL=clearHL, loop=0, wbinvd=wbinvd, nMeasurements=nMeasurements, agg=agg) if returnNbResults: nbResults[block].append(nb) hitEvent = 'L' + str(level) + '_HIT' diff --git a/tools/CacheAnalyzer/cacheSeq.py b/tools/CacheAnalyzer/cacheSeq.py index 471240e..88cf1e5 100755 --- a/tools/CacheAnalyzer/cacheSeq.py +++ b/tools/CacheAnalyzer/cacheSeq.py @@ -33,9 +33,8 @@ def main(): if args.sim: policyClass = cacheSim.AllPolicies[args.sim] - setCount = len(parseCacheSetsStr(args.level, (not args.noClearHL), args.sets)) seq = args.seq_init + (' ' + args.seq) * args.loop - hits = cacheSim.getHits(seq, policyClass, args.simAssoc, setCount) / args.loop + hits = cacheSim.getHits(seq, policyClass, args.simAssoc, args.sets) / args.loop print 'Hits: ' + str(hits) else: nb = runCacheExperiment(args.level, args.seq, initSeq=args.seq_init, cacheSets=args.sets, cBox=args.cBox, clearHL=(not args.noClearHL), loop=args.loop, diff --git a/tools/CacheAnalyzer/cacheSim.py b/tools/CacheAnalyzer/cacheSim.py index 1ab4418..74414ac 100755 --- a/tools/CacheAnalyzer/cacheSim.py +++ b/tools/CacheAnalyzer/cacheSim.py @@ -131,6 +131,12 @@ class LRU_PLRU4Sim(ReplPolicySim): self.PLRUOrdered = [curPLRU] + [plru for plru in self.PLRUOrdered if plru!=curPLRU] return hit + def flush(self, block): + for plru in self.PLRUs: + if block in plru.blocks: + plru.flush(block) + + class QLRUSim(ReplPolicySim): def __init__(self, assoc, hitFunc, missFunc, replIdxFunc, updFunc, updOnMissOnly=False): super(QLRUSim, self).__init__(assoc) @@ -298,18 +304,41 @@ AllRandPolicies = dict(AllRandQLRUVariants.items() + AllRandPLRUVariants.items() AllPolicies = dict(AllDetPolicies.items() + AllRandPolicies.items()) -def getHits(seq, policySimClass, assoc, nSets): - hits = 0 - policySims = [policySimClass(assoc) for _ in range(0, nSets)] +def parseCacheSetsStrSim(cacheSetsStr): + if cacheSetsStr is None: + raise ValueError('no cache sets specified') + cacheSetList = [] + for s in cacheSetsStr.split(','): + if '-' in s: + first, last = s.split('-')[:2] + cacheSetList += range(int(first), int(last)+1) + else: + cacheSetList.append(int(s)) + + return cacheSetList + + +def getHits(seq, policySimClass, assoc, cacheSetStr): + cacheSetList = parseCacheSetsStrSim(cacheSetStr) + allUsedSets = getAllUsedCacheSets(cacheSetList, seq) + policySims = {s: policySimClass(assoc) for s in allUsedSets} + + hits = 0 for blockStr in seq.split(): blockName = getBlockName(blockStr) - if '!' in blockStr: - for policySim in policySims: - policySim.flush(blockName) - else: - for policySim in policySims: - hit = policySim.acc(blockName) + if blockName == '': + policySims = {s: policySimClass(assoc) for s in allUsedSets} + continue + + overrideSet = getBlockSet(blockStr) + sets = [overrideSet] if overrideSet is not None else cacheSetList + + for s in sets: + if '!' in blockStr: + policySims[s].flush(blockName) + else: + hit = policySims[s].acc(blockName) if '?' in blockStr: hits += int(hit) return hits @@ -320,7 +349,7 @@ def getAges(blocks, seq, policySimClass, assoc): for block in blocks: for i in count(0): curSeq = seq + ' ' + ' '.join('N' + str(n) for n in range(0,i)) + ' ' + block + '?' - if getHits(curSeq, policySimClass, assoc, 1) == 0: + if getHits(curSeq, policySimClass, assoc, '0') == 0: ages[block] = i break return ages @@ -332,7 +361,7 @@ def getGraph(blocks, seq, policySimClass, assoc, maxAge, nSets=1, nRep=1, agg="m trace = [] for i in range(0, maxAge): curSeq = seq + ' ' + ' '.join('N' + str(n) for n in range(0,i)) + ' ' + block + '?' - hits = [getHits(curSeq, policySimClass, assoc, nSets) for _ in range(0, nRep)] + hits = [getHits(curSeq, policySimClass, assoc, '0-'+str(nSets-1)) for _ in range(0, nRep)] if agg == "med": aggValue = median(hits) elif agg == "min": diff --git a/tools/CacheAnalyzer/hitMiss.py b/tools/CacheAnalyzer/hitMiss.py index 559434f..81bbdbe 100755 --- a/tools/CacheAnalyzer/hitMiss.py +++ b/tools/CacheAnalyzer/hitMiss.py @@ -27,9 +27,9 @@ def main(): logging.basicConfig(stream=sys.stdout, format='%(message)s', level=logging.getLevelName(args.logLevel)) if args.sim: - policyClass = cacheSim.Policies[args.sim] + policyClass = cacheSim.AllPolicies[args.sim] seq = re.sub('[?!]', '', ' '.join([args.seq_init, args.seq])).strip() + '?' - hits = cacheSim.getHits(policyClass(args.simAssoc), seq) + hits = cacheSim.getHits(seq, policyClass, args.simAssoc, args.sets) if hits > 0: print 'HIT' exit(1) diff --git a/tools/CacheAnalyzer/replPolicy.py b/tools/CacheAnalyzer/replPolicy.py index 99fba0f..34609fe 100755 --- a/tools/CacheAnalyzer/replPolicy.py +++ b/tools/CacheAnalyzer/replPolicy.py @@ -1,6 +1,7 @@ #!/usr/bin/python import argparse import random +import sys from numpy import median @@ -17,13 +18,11 @@ def getActualHits(seq, level, cacheSets, cBox, nMeasurements=10): def findSmallCounterexample(policy, initSeq, level, sets, cBox, assoc, seq, nMeasurements): - setCount = len(parseCacheSetsStr(level, True, sets)) - seqSplit = seq.split() for seqPrefix in [seqSplit[:i] for i in range(assoc+1, len(seqSplit)+1)]: seq = initSeq + ' '.join(seqPrefix) actual = getActualHits(seq, level, sets, cBox, nMeasurements) - sim = cacheSim.getHits(seq, cacheSim.AllPolicies[policy], assoc, setCount) + sim = cacheSim.getHits(seq, cacheSim.AllPolicies[policy], assoc, sets) print 'seq:' + seq + ', actual: ' + str(actual) + ', sim: ' + str(sim) if sim != actual: break @@ -32,7 +31,7 @@ def findSmallCounterexample(policy, initSeq, level, sets, cBox, assoc, seq, nMea tmpPrefix = seqPrefix[:i] + seqPrefix[(i+1):] seq = initSeq + ' '.join(tmpPrefix) actual = getActualHits(seq, level, sets, cBox, nMeasurements) - sim = cacheSim.getHits(seq, cacheSim.AllPolicies[policy], assoc, setCount) + sim = cacheSim.getHits(seq, cacheSim.AllPolicies[policy], assoc, sets) print 'seq:' + seq + ', actual: ' + str(actual) + ', sim: ' + str(sim) if sim != actual: seqPrefix = tmpPrefix @@ -62,6 +61,7 @@ def main(): parser.add_argument("-sets", help="Cache sets (if not specified, all cache sets are used)") parser.add_argument("-cBox", help="cBox (default: 0)", type=int) parser.add_argument("-nMeasurements", help="Number of measurements", type=int, default=3) + parser.add_argument("-rep", help="Number of repetitions of each experiment (Default: 1)", type=int, default=1) parser.add_argument("-findCtrEx", help="Tries to find a small counterexample for each policy (only available for deterministic policies)", action='store_true') parser.add_argument("-policies", help="Comma-separated list of policies to consider (Default: all deterministic policies)") parser.add_argument("-best", help="Find the best matching policy (Default: abort if no policy agrees with all results)", action='store_true') @@ -83,6 +83,8 @@ def main(): elif args.allQLRUVariants: policies = sorted(set(cacheSim.CommonPolicies.keys())|set(cacheSim.AllDetQLRUVariants.keys())) elif args.randPolicies: + if args.rep > 1: + sys.exit('rep > 1 not supported for random policies') policies = sorted(cacheSim.AllRandPolicies.keys()) if args.assoc: @@ -94,8 +96,6 @@ def main(): if args.cBox: cBox = args.cBox - setCount = len(parseCacheSetsStr(args.level, True, args.sets)) - title = cpuid.cpu_name(cpuid.CPUID()) + ', Level: ' + str(args.level) + (', CBox: ' + str(cBox) if args.cBox else '') html = ['', '', '' + title + '', '', ''] @@ -117,25 +117,27 @@ def main(): print fullSeq html += ['' + fullSeq + ''] - actual = getActualHits(fullSeq, args.level, args.sets, cBox, args.nMeasurements) - html += ['' + str(actual) + ''] + actualHits = set([getActualHits(fullSeq, args.level, args.sets, cBox, args.nMeasurements) for _ in range(0, args.rep)]) + html += ['' + ('{' if len(actualHits) > 1 else '') + ', '.join(map(str, sorted(actualHits))) + ('}' if len(actualHits) > 1 else '') + ''] outp = '' for p in policies: if not args.randPolicies: - sim = cacheSim.getHits(fullSeq, cacheSim.AllPolicies[p], assoc, setCount) + sim = cacheSim.getHits(fullSeq, cacheSim.AllPolicies[p], assoc, args.sets) - if sim != actual: + if sim not in actualHits: possiblePolicies.discard(p) dists[p] += 1 color = 'red' if args.findCtrEx and not p in counterExamples: counterExamples[p] = findSmallCounterexample(p, ((args.initSeq + ' ') if args.initSeq else ''), args.level, args.sets, cBox, assoc, seq, args.nMeasurements) + elif len(actualHits) > 1: + color = 'yellow' else: color = 'green' else: - sim = median(sum(cacheSim.getHits(fullSeq, cacheSim.AllPolicies[p], assoc, setCount) for _ in range(0, args.nMeasurements))) + sim = median(sum(cacheSim.getHits(fullSeq, cacheSim.AllPolicies[p], assoc, args.sets) for _ in range(0, args.nMeasurements))) dist = (sim - actual) ** 2 dists[p] += dist