From 4e7954ff5d16d0416036d6c5cf44519c3350335a Mon Sep 17 00:00:00 2001 From: Andreas Abel Date: Tue, 7 Jan 2020 23:48:12 +0100 Subject: [PATCH] support for other slices --- tools/CacheAnalyzer/cacheGraph.py | 6 +- tools/CacheAnalyzer/cacheLib.py | 119 +++++++++++++++--------------- tools/CacheAnalyzer/cacheSeq.py | 5 +- tools/CacheAnalyzer/hitMiss.py | 5 +- tools/CacheAnalyzer/permPolicy.py | 14 ++-- tools/CacheAnalyzer/replPolicy.py | 17 +++-- tools/CacheAnalyzer/setDueling.py | 105 ++++++++++++++++---------- 7 files changed, 150 insertions(+), 121 deletions(-) diff --git a/tools/CacheAnalyzer/cacheGraph.py b/tools/CacheAnalyzer/cacheGraph.py index 0100c2a..c75f673 100755 --- a/tools/CacheAnalyzer/cacheGraph.py +++ b/tools/CacheAnalyzer/cacheGraph.py @@ -38,6 +38,7 @@ def main(): parser.add_argument("-nMeasurements", help="Number of measurements", type=int, default=10) parser.add_argument("-agg", help="Aggregate function", default='med') parser.add_argument("-cBox", help="cBox (default: 1)", type=int, default=1) + parser.add_argument("-slice", help="Slice (within the cBox) (default: 0)", type=int, default=0) parser.add_argument("-logLevel", help="Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)", default='WARNING') parser.add_argument("-blocks", help="Blocks to consider (default: all blocks in seq)") parser.add_argument("-maxAge", help="Maximum age", type=int) @@ -69,8 +70,9 @@ def main(): title = 'Access Sequence: ' + args.seq.replace('?','').strip() + ' ?' html.append(getPlotlyGraphDiv(title, '# of fresh blocks', 'Hits', traces)) else: - _, nbDict = getAgesOfBlocks(blocks, args.level, args.seq, initSeq=args.seq_init, cacheSets=args.sets, cBox=args.cBox, clearHL=(not args.noClearHL), - wbinvd=(not args.noWbinvd), returnNbResults=True, maxAge=args.maxAge, nMeasurements=args.nMeasurements, agg=args.agg) + _, nbDict = getAgesOfBlocks(blocks, args.level, args.seq, initSeq=args.seq_init, cacheSets=args.sets, cBox=args.cBox, cSlice=args.slice, + clearHL=(not args.noClearHL), wbinvd=(not args.noWbinvd), returnNbResults=True, maxAge=args.maxAge, + nMeasurements=args.nMeasurements, agg=args.agg) for event in sorted(e for e in nbDict.values()[0][0].keys() if 'HIT' in e or 'MISS' in e): traces = [(b, [nb[event] for nb in nbDict[b]]) for b in blocks] title = 'Access Sequence: ' + (args.seq_init + ' ' + args.seq).replace('?','').strip() + ' ?' diff --git a/tools/CacheAnalyzer/cacheLib.py b/tools/CacheAnalyzer/cacheLib.py index c59f116..254ec43 100755 --- a/tools/CacheAnalyzer/cacheLib.py +++ b/tools/CacheAnalyzer/cacheLib.py @@ -282,7 +282,7 @@ def getCodeForAddressLists(codeAddressLists, initAddressLists=[], wbinvd=False, pfcEnabled = True # use multiple lfence instructions to make sure that the block is actually in the cache and not still in a fill buffer - codeList.append('lfence; ' * 10) + codeList.append('lfence; ' * 25) if addressList.flush: for address in addresses: @@ -353,7 +353,7 @@ def getClearHLAddresses(level, cacheSetList, cBox=1): return clearAddresses L3SetToWayIDMap = dict() -def getAddresses(level, wayID, cacheSetList, cBox=1, clearHL=True): +def getAddresses(level, wayID, cacheSetList, cBox=1, cSlice=0, clearHL=True): lineSize = getCacheInfo(1).lineSize if level <= 2 or (level == 3 and getCacheInfo(3).nSlices is None): @@ -363,20 +363,22 @@ def getAddresses(level, wayID, cacheSetList, cBox=1, clearHL=True): elif level == 3: if not cBox in L3SetToWayIDMap: L3SetToWayIDMap[cBox] = dict() + if not cSlice in L3SetToWayIDMap[cBox]: + L3SetToWayIDMap[cBox][cSlice] = dict() addresses = [] for L3Set in cacheSetList: - if not L3Set in L3SetToWayIDMap[cBox]: - L3SetToWayIDMap[cBox][L3Set] = dict() + if not L3Set in L3SetToWayIDMap[cBox][cSlice]: + L3SetToWayIDMap[cBox][cSlice][L3Set] = dict() if getCacheInfo(3).nSlices != getNCBoxUnits(): - for i, addr in enumerate(findMinimalL3EvictionSet(L3Set, cBox)): - L3SetToWayIDMap[cBox][L3Set][i] = addr - if not wayID in L3SetToWayIDMap[cBox][L3Set]: + for i, addr in enumerate(findMinimalL3EvictionSet(L3Set, cBox, cSlice)): + L3SetToWayIDMap[cBox][cSlice][L3Set][i] = addr + if not wayID in L3SetToWayIDMap[cBox][cSlice][L3Set]: if getCacheInfo(3).nSlices == getNCBoxUnits(): - L3SetToWayIDMap[cBox][L3Set][wayID] = next(iter(getNewAddressesInCBox(1, cBox, L3Set, L3SetToWayIDMap[cBox][L3Set].values()))) + L3SetToWayIDMap[cBox][cSlice][L3Set][wayID] = next(iter(getNewAddressesInCBox(1, cBox, L3Set, L3SetToWayIDMap[cBox][cSlice][L3Set].values()))) else: - L3SetToWayIDMap[cBox][L3Set][wayID] = next(iter(findCongruentL3Addresses(1, L3Set, cBox, L3SetToWayIDMap[cBox][L3Set].values()))) - addresses.append(L3SetToWayIDMap[cBox][L3Set][wayID]) + L3SetToWayIDMap[cBox][cSlice][L3Set][wayID] = next(iter(findCongruentL3Addresses(1, L3Set, cBox, L3SetToWayIDMap[cBox][cSlice][L3Set].values()))) + addresses.append(L3SetToWayIDMap[cBox][cSlice][L3Set][wayID]) return addresses @@ -436,7 +438,7 @@ def getAllUsedCacheSets(cacheSetList, seq, initSeq=''): AddressList = namedtuple('AddressList', 'addresses exclude flush wbinvd') -def getCodeForCacheExperiment(level, seq, initSeq, cacheSetList, cBox, clearHL, wbinvd): +def getCodeForCacheExperiment(level, seq, initSeq, cacheSetList, cBox, cSlice, clearHL, wbinvd): allUsedSets = getAllUsedCacheSets(cacheSetList, seq, initSeq) clearHLAddrList = None @@ -461,7 +463,7 @@ def getCodeForCacheExperiment(level, seq, initSeq, cacheSetList, cBox, clearHL, flush = '!' in seqEl s = [overrideSet] if overrideSet is not None else cacheSetList - addresses = getAddresses(level, wayID, s, cBox=cBox, clearHL=clearHL) + addresses = getAddresses(level, wayID, s, cBox=cBox, cSlice=cSlice, clearHL=clearHL) if clearHLAddrList is not None and not flush: addrLists.append(clearHLAddrList) @@ -482,11 +484,12 @@ def runCacheExperimentCode(code, initCode, oneTimeInitCode, loop, warmUpCount, c # cacheSets=None means do access in all sets # in this case, the first nL1Sets many sets of L2 will be reserved for clearing L1 +# cSlice refers to the nth slice within a given cBox; the assigment of numbers to slices is arbitrary # 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'): +def runCacheExperiment(level, seq, initSeq='', cacheSets=None, cBox=1, cSlice=0, 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) + ec = getCodeForCacheExperiment(level, seq, initSeq=initSeq, cacheSetList=cacheSetList, cBox=cBox, cSlice=cSlice, clearHL=clearHL, wbinvd=wbinvd) log.debug('\nOneTimeInit: ' + ec.oneTimeInit) log.debug('\nInit: ' + ec.init) @@ -504,71 +507,69 @@ def printNB(nb_result): print r[0] + ': ' + str(r[1]) -def findMinimalL3EvictionSet(cacheSet, cBox): - setNanoBenchParameters(config='\n'.join([getEventConfig('L3_HIT'), getEventConfig('L3_MISS')]), msrConfig=None, nMeasurements=10, unrollCount=1, loopCount=10, - warmUpCount=None, initialWarmUpCount=None, aggregateFunction='med', basicMode=True, noMem=True, verbose=None) +def hasL3Conflicts(addresses, clearHLAddrList, codeOffset): + addrList = AddressList(addresses, False, False, False) + ec = getCodeForAddressLists([clearHLAddrList, addrList], initAddressLists=[addrList], wbinvd=True) + setNanoBenchParameters(config=getEventConfig('L3_HIT'), msrConfig='', nMeasurements=5, unrollCount=1, loopCount=100, + aggregateFunction='med', basicMode=True, noMem=True, codeOffset=codeOffset) + nb = runNanoBench(code=ec.code, init=ec.init, oneTimeInit=ec.oneTimeInit) + + return (nb['L3_HIT'] < len(addresses) - .9) + + +def findMinimalL3EvictionSet(cacheSet, cBox, cSlice): if not hasattr(findMinimalL3EvictionSet, 'evSetForCacheSet'): findMinimalL3EvictionSet.evSetForCacheSet = dict() if not cBox in findMinimalL3EvictionSet.evSetForCacheSet: findMinimalL3EvictionSet.evSetForCacheSet[cBox] = dict() - evSetForCacheSet = findMinimalL3EvictionSet.evSetForCacheSet[cBox] + if not cSlice in findMinimalL3EvictionSet.evSetForCacheSet[cBox]: + findMinimalL3EvictionSet.evSetForCacheSet[cBox][cSlice] = dict() - if cacheSet in evSetForCacheSet: - return evSetForCacheSet[cacheSet] + if cacheSet in findMinimalL3EvictionSet.evSetForCacheSet[cBox][cSlice]: + return findMinimalL3EvictionSet.evSetForCacheSet[cBox][cSlice][cacheSet] + + evSetsForOtherSlices = [findMinimalL3EvictionSet(cacheSet, cBox, s) for s in range(0, cSlice)] + + lineSize = getCacheInfo(1).lineSize + L3Assoc = getCacheInfo(3).assoc + L3WaySize = getCacheInfo(3).waySize clearHLAddrList = AddressList(getClearHLAddresses(3, [cacheSet], cBox), True, False, False) + codeOffset = lineSize * (cacheSet+10) + addresses = [] - curAddress = cacheSet*getCacheInfo(3).lineSize + for curAddr in count(cacheSet * lineSize, L3WaySize): + if any(curAddr in otherEvSet for otherEvSet in evSetsForOtherSlices): continue + if not getCBoxOfAddress(curAddr) == cBox: continue + if any(hasL3Conflicts(otherEvSet[:-1]+[curAddr], clearHLAddrList, codeOffset) for otherEvSet in evSetsForOtherSlices): continue - while len(addresses) < getCacheInfo(3).assoc: - curAddress += getCacheInfo(3).waySize - if getCBoxOfAddress(curAddress) == cBox: - addresses.append(curAddress) - - while True: - curAddress += getCacheInfo(3).waySize - if not getCBoxOfAddress(curAddress) == cBox: continue - - addresses += [curAddress] - ec = getCodeForAddressLists([AddressList(addresses, False, False, False), clearHLAddrList]) - - setNanoBenchParameters(config=getDefaultCacheConfig(), msrConfig='', nMeasurements=10, unrollCount=1, loopCount=100, - aggregateFunction='med', basicMode=True, noMem=True) - nb = runNanoBench(code=ec.code, oneTimeInit=ec.oneTimeInit) - - if nb['L3_HIT'] < len(addresses) - .9: + addresses.append(curAddr) + if len(addresses) > L3Assoc and hasL3Conflicts(addresses, clearHLAddrList, codeOffset): break for i in reversed(range(0, len(addresses))): + if len(addresses) <= L3Assoc+1: + break tmpAddresses = addresses[:i] + addresses[(i+1):] - - ec = getCodeForAddressLists([AddressList(tmpAddresses, False, False, False), clearHLAddrList]) - nb = runNanoBench(code=ec.code, oneTimeInit=ec.oneTimeInit) - - if nb['L3_HIT'] < len(tmpAddresses) - 0.9: + if hasL3Conflicts(tmpAddresses, clearHLAddrList, codeOffset): addresses = tmpAddresses - evSetForCacheSet[cacheSet] = addresses + findMinimalL3EvictionSet.evSetForCacheSet[cBox][cSlice][cacheSet] = addresses return addresses def findCongruentL3Addresses(n, cacheSet, cBox, L3EvictionSet): clearHLAddrList = AddressList(getClearHLAddresses(3, [cacheSet], cBox), True, False, False) - - congrAddresses = [] + codeOffset = getCacheInfo(1).lineSize * (cacheSet+10) L3WaySize = getCacheInfo(3).waySize + congrAddresses = [] for newAddr in count(max(L3EvictionSet)+L3WaySize, L3WaySize): if not getCBoxOfAddress(newAddr) == cBox: continue tmpAddresses = L3EvictionSet[:getCacheInfo(3).assoc] + [newAddr] - ec = getCodeForAddressLists([AddressList(tmpAddresses, False, False, False), clearHLAddrList]) - setNanoBenchParameters(config=getEventConfig('L3_HIT'), msrConfig=None, nMeasurements=10, unrollCount=1, loopCount=100, - aggregateFunction='med', basicMode=True, noMem=True, verbose=None) - nb = runNanoBench(code=ec.code, oneTimeInit=ec.oneTimeInit) - - if nb['L3_HIT'] < len(tmpAddresses) - 0.9: + if hasL3Conflicts(tmpAddresses, clearHLAddrList, codeOffset): congrAddresses.append(newAddr) if len(congrAddresses) >= n: break @@ -601,13 +602,8 @@ def findMaximalNonEvictingL3SetInCBox(start, stride, L3Assoc, cBox): continue newAddresses = addresses + [curAddress] - ec = getCodeForAddressLists([AddressList(newAddresses, False, False, False), clearHLAddrList]) - setNanoBenchParameters(config=getEventConfig('L3_HIT'), msrConfig='', nMeasurements=10, unrollCount=1, loopCount=10, - aggregateFunction='med', basicMode=True, noMem=True) - nb = runNanoBench(code=ec.code, oneTimeInit=ec.oneTimeInit) - - if nb['L3_HIT'] > len(newAddresses) - .9: + if not hasL3Conflicts(newAddresses, clearHLAddrList, start+getCacheInfo(1).lineSize): addresses = newAddresses notAdded = 0 else: @@ -628,7 +624,7 @@ def getUnusedBlockNames(n, usedBlockNames, prefix=''): # Returns a dict with the age of each block, i.e., how many fresh blocks need to be accessed until the block is evicted # if returnNbResults is True, the function returns additionally all measurment results (as the second component of a tuple) -def getAgesOfBlocks(blocks, level, seq, initSeq='', maxAge=None, cacheSets=None, cBox=1, clearHL=True, wbinvd=False, returnNbResults=False, nMeasurements=10, agg='avg'): +def getAgesOfBlocks(blocks, level, seq, initSeq='', maxAge=None, cacheSets=None, cBox=1, cSlice=0, clearHL=True, wbinvd=False, returnNbResults=False, nMeasurements=10, agg='avg'): ages = dict() if returnNbResults: nbResults = dict() @@ -645,7 +641,8 @@ 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, agg=agg) + nb = runCacheExperiment(level, curSeq, initSeq=initSeq, cacheSets=cacheSets, cBox=cBox, cSlice=cSlice, 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 88cf1e5..8e86974 100755 --- a/tools/CacheAnalyzer/cacheSeq.py +++ b/tools/CacheAnalyzer/cacheSeq.py @@ -19,6 +19,7 @@ def main(): parser.add_argument("-level", help="Cache level (Default: 1)", type=int, default=1) parser.add_argument("-sets", help="Cache sets (if not specified, all cache sets are used)") parser.add_argument("-cBox", help="cBox (default: 1)", type=int, default=1) # use 1 as default, as, e.g., on SNB, box 0 only has 15 ways instead of 16 + parser.add_argument("-slice", help="Slice (within the cBox) (default: 0)", type=int, default=0) parser.add_argument("-noClearHL", help="Do not clear higher levels", action='store_true') parser.add_argument("-nMeasurements", help="Number of measurements", type=int, default=10) parser.add_argument("-agg", help="Aggregate function", default='med') @@ -37,8 +38,8 @@ def main(): 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, - wbinvd=(not args.noWbinvd), nMeasurements=args.nMeasurements, agg=args.agg) + nb = runCacheExperiment(args.level, args.seq, initSeq=args.seq_init, cacheSets=args.sets, cBox=args.cBox, cSlice=args.slice, clearHL=(not args.noClearHL), + loop=args.loop, wbinvd=(not args.noWbinvd), nMeasurements=args.nMeasurements, agg=args.agg) printNB(nb) diff --git a/tools/CacheAnalyzer/hitMiss.py b/tools/CacheAnalyzer/hitMiss.py index 81bbdbe..0bd5666 100755 --- a/tools/CacheAnalyzer/hitMiss.py +++ b/tools/CacheAnalyzer/hitMiss.py @@ -16,6 +16,7 @@ def main(): parser.add_argument("-level", help="Cache level (Default: 1)", type=int, default=1) parser.add_argument("-sets", help="Cache sets (if not specified, all cache sets are used)") parser.add_argument("-cBox", help="cBox (default: 1)", type=int, default=1) # use 1 as default, as, e.g., on SNB, box 0 only has 15 ways instead of 16 + parser.add_argument("-slice", help="Slice (within the cBox) (default: 0)", type=int, default=0) parser.add_argument("-noClearHL", help="Do not clear higher levels", action='store_true') parser.add_argument("-loop", help="Loop count (Default: 1)", type=int, default=1) parser.add_argument("-noWbinvd", help="Do not call wbinvd before each run", action='store_true') @@ -39,8 +40,8 @@ def main(): else: setCount = len(parseCacheSetsStr(args.level, True, args.sets)) seq = re.sub('[?!]', '', args.seq).strip() + '?' - nb = runCacheExperiment(args.level, seq, initSeq=args.seq_init, cacheSets=args.sets, cBox=args.cBox, clearHL=(not args.noClearHL), loop=args.loop, - wbinvd=(not args.noWbinvd)) + nb = runCacheExperiment(args.level, seq, initSeq=args.seq_init, cacheSets=args.sets, cBox=args.cBox, cSlice=args.slice, clearHL=(not args.noClearHL), + loop=args.loop, wbinvd=(not args.noWbinvd)) if nb['L' + str(args.level) + '_HIT']/setCount > .5: print 'HIT' exit(1) diff --git a/tools/CacheAnalyzer/permPolicy.py b/tools/CacheAnalyzer/permPolicy.py index a14a160..951ecb1 100755 --- a/tools/CacheAnalyzer/permPolicy.py +++ b/tools/CacheAnalyzer/permPolicy.py @@ -20,7 +20,7 @@ import logging log = logging.getLogger(__name__) -def getPermutations(level, html, cacheSets=None, getInitialAges=True, maxAge=None, cBox=1): +def getPermutations(level, html, cacheSets=None, getInitialAges=True, maxAge=None, cBox=1, cSlice=0): assoc = getCacheInfo(level).assoc if not maxAge: maxAge=2*assoc @@ -32,7 +32,8 @@ def getPermutations(level, html, cacheSets=None, getInitialAges=True, maxAge=Non initBlocks = ['I' + str(i) for i in range(0, assoc)] seq = ' '.join(initBlocks) - initAges, nbDict = getAgesOfBlocks(initBlocks, level, seq, cacheSets=cacheSets, clearHL=True, wbinvd=True, returnNbResults=True, maxAge=maxAge, cBox=cBox) + initAges, nbDict = getAgesOfBlocks(initBlocks, level, seq, cacheSets=cacheSets, clearHL=True, wbinvd=True, returnNbResults=True, maxAge=maxAge, + cBox=cBox, cSlice=cSlice) accSeqStr = 'Access sequence: ' + seq print accSeqStr @@ -47,7 +48,8 @@ def getPermutations(level, html, cacheSets=None, getInitialAges=True, maxAge=Non blocks = ['B' + str(i) for i in range(0, assoc)] baseSeq = ' '.join(initBlocks + blocks) - ages, nbDict = getAgesOfBlocks(blocks, level, baseSeq, cacheSets=cacheSets, clearHL=True, wbinvd=True, returnNbResults=True, maxAge=maxAge, cBox=cBox) + ages, nbDict = getAgesOfBlocks(blocks, level, baseSeq, cacheSets=cacheSets, clearHL=True, wbinvd=True, returnNbResults=True, maxAge=maxAge, + cBox=cBox, cSlice=cSlice) accSeqStr = 'Access sequence: ' + baseSeq print accSeqStr @@ -61,7 +63,8 @@ def getPermutations(level, html, cacheSets=None, getInitialAges=True, maxAge=Non for permI, permBlock in enumerate(blocksSortedByAge): seq = baseSeq + ' ' + permBlock - permAges, nbDict = getAgesOfBlocks(blocks, level, seq, cacheSets=cacheSets, clearHL=True, wbinvd=True, returnNbResults=True, maxAge=maxAge, cBox=cBox) + permAges, nbDict = getAgesOfBlocks(blocks, level, seq, cacheSets=cacheSets, clearHL=True, wbinvd=True, returnNbResults=True, maxAge=maxAge, + cBox=cBox, cSlice=cSlice) accSeqStr = 'Access sequence: ' + seq traces = [(b, [nb[event] for nb in nbDict[b]]) for b in blocks] @@ -84,6 +87,7 @@ def main(): parser.add_argument("-noInit", help="Do not fill sets with associativity many elements first", action='store_true') parser.add_argument("-maxAge", help="Maximum age", type=int) parser.add_argument("-cBox", help="cBox (default: 1)", type=int, default=1) + parser.add_argument("-slice", help="Slice (within the cBox) (default: 0)", type=int, default=0) parser.add_argument("-sim", help="Simulate the given policy instead of running the experiment on the hardware") parser.add_argument("-simAssoc", help="Associativity of the simulated cache (default: 8)", type=int, default=8) parser.add_argument("-logLevel", help="Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)", default='WARNING') @@ -97,7 +101,7 @@ def main(): html = ['', '', '' + title + '', '', '', ''] html += ['

' + title + '

'] - getPermutations(args.level, html, cacheSets=args.sets, getInitialAges=(not args.noInit), maxAge=args.maxAge, cBox=args.cBox) + getPermutations(args.level, html, cacheSets=args.sets, getInitialAges=(not args.noInit), maxAge=args.maxAge, cBox=args.cBox, cSlice=args.slice) html += ['', ''] with open(args.output ,'w') as f: diff --git a/tools/CacheAnalyzer/replPolicy.py b/tools/CacheAnalyzer/replPolicy.py index 34609fe..53c5c28 100755 --- a/tools/CacheAnalyzer/replPolicy.py +++ b/tools/CacheAnalyzer/replPolicy.py @@ -12,16 +12,16 @@ import logging log = logging.getLogger(__name__) -def getActualHits(seq, level, cacheSets, cBox, nMeasurements=10): - nb = runCacheExperiment(level, seq, cacheSets=cacheSets, cBox=cBox, clearHL=True, loop=1, wbinvd=True, nMeasurements=nMeasurements, agg='med') +def getActualHits(seq, level, cacheSets, cBox, cSlice, nMeasurements=10): + nb = runCacheExperiment(level, seq, cacheSets=cacheSets, cBox=cBox, cSlice=cSlice, clearHL=True, loop=1, wbinvd=True, nMeasurements=nMeasurements, agg='med') return int(nb['L' + str(level) + '_HIT']+0.1) -def findSmallCounterexample(policy, initSeq, level, sets, cBox, assoc, seq, nMeasurements): +def findSmallCounterexample(policy, initSeq, level, sets, cBox, cSlice, assoc, seq, nMeasurements): 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) + actual = getActualHits(seq, level, sets, cBox, cSlice, nMeasurements) sim = cacheSim.getHits(seq, cacheSim.AllPolicies[policy], assoc, sets) print 'seq:' + seq + ', actual: ' + str(actual) + ', sim: ' + str(sim) if sim != actual: @@ -30,7 +30,7 @@ def findSmallCounterexample(policy, initSeq, level, sets, cBox, assoc, seq, nMea for i in reversed(range(0, len(seqPrefix)-1)): tmpPrefix = seqPrefix[:i] + seqPrefix[(i+1):] seq = initSeq + ' '.join(tmpPrefix) - actual = getActualHits(seq, level, sets, cBox, nMeasurements) + actual = getActualHits(seq, level, sets, cBox, cSlice, nMeasurements) sim = cacheSim.getHits(seq, cacheSim.AllPolicies[policy], assoc, sets) print 'seq:' + seq + ', actual: ' + str(actual) + ', sim: ' + str(sim) if sim != actual: @@ -60,6 +60,7 @@ def main(): parser.add_argument("-level", help="Cache level (Default: 1)", type=int, default=1) 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("-slice", help="Slice (within the cBox) (default: 0)", type=int, default=0) 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') @@ -117,7 +118,7 @@ def main(): print fullSeq html += ['' + fullSeq + ''] - actualHits = set([getActualHits(fullSeq, args.level, args.sets, cBox, args.nMeasurements) for _ in range(0, args.rep)]) + actualHits = set([getActualHits(fullSeq, args.level, args.sets, cBox, args.slice, 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 = '' @@ -130,8 +131,8 @@ def main(): 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) + counterExamples[p] = findSmallCounterexample(p, ((args.initSeq + ' ') if args.initSeq else ''), args.level, args.sets, cBox, args.slice, + assoc, seq, args.nMeasurements) elif len(actualHits) > 1: color = 'yellow' else: diff --git a/tools/CacheAnalyzer/setDueling.py b/tools/CacheAnalyzer/setDueling.py index a44b2a7..ad20263 100755 --- a/tools/CacheAnalyzer/setDueling.py +++ b/tools/CacheAnalyzer/setDueling.py @@ -16,6 +16,8 @@ def main(): parser.add_argument("-level", help="Cache level (Default: 3)", type=int, default=3) parser.add_argument("-nRuns", help="Maximum number of runs", type=int, default=25) parser.add_argument("-loop", help="Loop count", type=int, default=25) + parser.add_argument("-length", help="Length of the acc. seq. (Default: associativity*4/3)", type=int) + parser.add_argument("-noClearHL", help="Do not clear higher levels", action='store_true') parser.add_argument("-nMeasurements", help="Number of measurements", type=int, default=10) parser.add_argument("-output", help="Output file name", default='setDueling.html') parser.add_argument("-logLevel", help="Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)", default='INFO') @@ -27,64 +29,85 @@ def main(): nSets = getCacheInfo(args.level).nSets lineSize = getCacheInfo(1).lineSize nCBoxes = max(1, getNCBoxUnits()) - seq = ' '.join('B' + str(i) + '?' for i in range(0, assoc*4/3)) + nSlicesPerCBox = 1 + if getCacheInfo(3).nSlices: + nSlicesPerCBox = getCacheInfo(3).nSlices / getCacheInfo(3).nCboxes - title = cpuid.cpu_name(cpuid.CPUID()) + ', Level: ' + str(args.level) + seqLength = (args.length if args.length is not None else assoc*4/3) + seq = ' '.join('B' + str(i) + '?' for i in range(0, seqLength)) + hitSeq = ' '.join('B' + str(i) + '?' for i in range(0, assoc)) + missSeq = ' '.join('B' + str(i) + '?' for i in range(0, 3*assoc)) + + title = cpuid.cpu_name(cpuid.CPUID()) + ', L' + str(args.level) + ' Hits' html = ['', '', '' + title + '', '', '', ''] html += ['

' + title + '

'] - setsForCBox = {cBox: range(0,nSets) for cBox in range(0, nCBoxes)} - yValuesForCBox = {cBox: [[] for s in range(0, nSets)] for cBox in range(0, nCBoxes)} + setsForSlice = {cBox: {cSlice: range(0,nSets) for cSlice in range(0, nSlicesPerCBox)} for cBox in range(0, nCBoxes)} + yValuesForSlice = {cBox: {cSlice: [[] for s in range(0, nSets)] for cSlice in range(0, nSlicesPerCBox)} for cBox in range(0, nCBoxes)} + prevOti = '' i = -1 notChanged = -1 - prevEc = ExperimentCode('', '', '') - while notChanged < 10: - i += 1 - notChanged += 1 + for useHitSeq in [False, True]: + i += 1 + notChanged += 1 + for cBox in range(0, nCBoxes): + for cSlice in range(0, nSlicesPerCBox): + yValuesList = yValuesForSlice[cBox][cSlice] - for cBox in range(0, nCBoxes): - yValuesList = yValuesForCBox[cBox] + curSets = setsForSlice[cBox][cSlice] + random.shuffle(curSets) + prevSets = curSets[:] - curSets = setsForCBox[cBox] - random.shuffle(curSets) - prevSets = curSets[:] + for si, s in enumerate(prevSets): + codeSet = (s + random.randint(1, nSets - 100)) % nSets + codeOffset = lineSize * codeSet + yv = yValuesList[s] - for si, s in enumerate(prevSets): - codeSet = (s + random.randint(1, nSets - 100)) % nSets - codeOffset = lineSize * codeSet + ec = getCodeForCacheExperiment(args.level, seq, '', [s], cBox, cSlice, (not args.noClearHL), True) + nb = runCacheExperimentCode(ec.code, ec.init, prevOti + ec.oneTimeInit, loop=args.loop, warmUpCount=0, codeOffset=codeOffset, + nMeasurements=args.nMeasurements, agg='med') + yv.append(nb['L' + str(args.level) + '_HIT']) + yv.sort() - ec = getCodeForCacheExperiment(args.level, seq, initSeq='', cacheSetList=[s], cBox=cBox, clearHL=True, wbinvd=True) - oneTimeInit = prevEc.oneTimeInit + 'mov R15, ' + str(args.loop*args.nMeasurements) + '; pLoop:' + prevEc.code + '; dec R15; jnz pLoop; ' + ec.oneTimeInit + yvStr = str(yv) if len(yv) <= 5 else '[%s, %s, ..., %s, %s]' % (yv[0], yv[1], yv[-2], yv[-1]) + log.info('CBox ' + str(cBox) + ', slice: ' + str(cSlice) + ', run ' + str(i) + ', set: ' + str(si+1) + '/' + str(len(prevSets)) + + ' (' + str(s) + '), ' + yvStr) - nb = runCacheExperimentCode(ec.code, ec.init, oneTimeInit, loop=args.loop, warmUpCount=0, codeOffset=codeOffset, nMeasurements=args.nMeasurements, agg='med') - hits = nb['L' + str(args.level) + '_HIT'] - - yv = yValuesList[s] - yv.append(hits) - yv.sort() - - yvStr = str(yv) if len(yv) <= 5 else '[%s, %s, ..., %s, %s]' % (yv[0], yv[1], yv[-2], yv[-1]) - log.info('CBox ' + str(cBox) + ', run ' + str(i) + ', set: ' + str(si+1) + '/' + str(len(prevSets)) + ' (' + str(s) + '), ' + yvStr) - - if len(yv) >= 4 and yv[-1]-yv[0] > .5 and abs(yv[0]-yv[1]) < .5 and abs(yv[-1]-yv[-2]) < .5: #max(yValuesList[s]) - min(yValuesList[s]) > 0.1: #max(yValuesList[s]) > 2 and min(yValuesList[s]) < assoc/2: - curSets.remove(s) - notChanged = 0 - - if yv[-1]-yv[0] < .5: - prevEc = ec + if len(yv) > 1: + if yv[-1]-yv[0] > 1: + curSets.remove(s) + notChanged = 0 + else: + if useHitSeq: + ec = getCodeForCacheExperiment(args.level, hitSeq, '', [s], cBox, cSlice, (not args.noClearHL), True) + else: + ec = getCodeForCacheExperiment(args.level, missSeq, '', [s], cBox, cSlice, (not args.noClearHL), True) + prevOti = ec.oneTimeInit + 'mov R15, 100; pLoop:' + ec.code + '; dec R15; jnz pLoop; ' for cBox in range(0, nCBoxes): - yValues = [min(x) + (max(x)-min(x))/2 for x in yValuesForCBox[cBox] if x] + for cSlice in range(0, nSlicesPerCBox): + fig = go.Figure() - fig = go.Figure() - fig.update_layout(title_text='CBox ' + str(cBox) + ', Sequence (accessed ' + str(args.loop) + ' times in each set): ' + seq) - fig.update_layout(showlegend=True) - fig.update_xaxes(title_text='Set') - fig.add_trace(go.Scatter(y=yValues, mode='lines+markers', name='L3 Hits')) + title = 'CBox ' + str(cBox) + if nSlicesPerCBox > 1: title += ', Slice: ' + str(cSlice) + title += ', Sequence (accessed ' + str(args.loop) + ' times in each set): ' + seq + fig.update_layout(title_text=title) - html.append(plot(fig, include_plotlyjs=False, output_type='div')) + fig.update_layout(showlegend=True) + fig.update_xaxes(title_text='Set') + + yValuesMinMax = [min(x) + (max(x)-min(x))/2 for x in yValuesForSlice[cBox][cSlice] if x] + fig.add_trace(go.Scatter(y=yValuesMinMax, mode='lines+markers', name='Min+(Max-Min)/2')) + + yValuesMin = [min(x) for x in yValuesForSlice[cBox][cSlice] if x] + fig.add_trace(go.Scatter(y=yValuesMin, mode='lines+markers', visible = 'legendonly', name='Min')) + + yValuesMax = [max(x) for x in yValuesForSlice[cBox][cSlice] if x] + fig.add_trace(go.Scatter(y=yValuesMax, mode='lines+markers', visible = 'legendonly', name='Max')) + + html.append(plot(fig, include_plotlyjs=False, output_type='div')) html += ['', '']