diff --git a/configs/example/memtest.py b/configs/example/memtest.py index 9a66320d8..0df9f766f 100644 --- a/configs/example/memtest.py +++ b/configs/example/memtest.py @@ -40,11 +40,20 @@ # Andreas Hansson import optparse +import random import sys import m5 from m5.objects import * +# This example script stress tests the memory system by creating false +# sharing in a tree topology. At the bottom of the tree is a shared +# memory, and then at each level a number of testers are attached, +# along with a number of caches that them selves fan out to subtrees +# of testers and caches. Thus, it is possible to create a system with +# arbitrarily deep cache hierarchies, sharing or no sharing of caches, +# and testers not only at the L1s, but also at the L2s, L3s etc. + parser = optparse.OptionParser() parser.add_option("-a", "--atomic", action="store_true", @@ -57,14 +66,6 @@ parser.add_option("-m", "--maxtick", type="int", default=m5.MaxTick, metavar="T", help="Stop after T ticks") -# This example script stress tests the memory system by creating false -# sharing in a tree topology. At the bottom of the tree is a shared -# memory, and then at each level a number of testers are attached, -# along with a number of caches that them selves fan out to subtrees -# of testers and caches. Thus, it is possible to create a system with -# arbitrarily deep cache hierarchies, sharing or no sharing of caches, -# and testers not only at the L1s, but also at the L2s, L3s etc. -# # The tree specification consists of two colon-separated lists of one # or more integers, one for the caches, and one for the testers. The # first integer is the number of caches/testers closest to main @@ -92,8 +93,9 @@ parser.add_option("-u", "--uncacheable", type="int", default=0, metavar="PCT", help="Target percentage of uncacheable accesses " "[default: %default]") - -parser.add_option("--progress", type="int", default=10000, +parser.add_option("-r", "--random", action="store_true", + help="Generate a random tree topology") +parser.add_option("--progress", type="int", default=100000, metavar="NLOADS", help="Progress message interval " "[default: %default]") @@ -108,55 +110,69 @@ if args: print "Error: script doesn't take any positional arguments" sys.exit(1) +# Get the total number of testers +def numtesters(cachespec, testerspec): + # Determine the tester multiplier for each level as the + # elements are per subsystem and it fans out + multiplier = [1] + for c in cachespec: + multiplier.append(multiplier[-1] * c) + + total = 0 + for t, m in zip(testerspec, multiplier): + total += t * m + + return total + block_size = 64 # Start by parsing the command line options and do some basic sanity # checking -try: - cachespec = [int(x) for x in options.caches.split(':')] - testerspec = [int(x) for x in options.testers.split(':')] -except: - print "Error: Unable to parse caches or testers option" - sys.exit(1) +if options.random: + # Generate a tree with a valid number of testers + while True: + tree_depth = random.randint(1, 4) + cachespec = [random.randint(1, 3) for i in range(tree_depth)] + testerspec = [random.randint(1, 3) for i in range(tree_depth + 1)] + if numtesters(cachespec, testerspec) < block_size: + break -if len(cachespec) < 1: - print "Error: Must have at least one level of caches" - sys.exit(1) - -if len(cachespec) != len(testerspec) - 1: - print "Error: Testers must have one element more than caches" - sys.exit(1) - -if testerspec[-1] == 0: - print "Error: Must have testers at the uppermost level" - sys.exit(1) - -for t in testerspec: - if t < 0: - print "Error: Cannot have a negative number of testers" + print "Generated random tree -c", ':'.join(map(str, cachespec)), \ + "-t", ':'.join(map(str, testerspec)) +else: + try: + cachespec = [int(x) for x in options.caches.split(':')] + testerspec = [int(x) for x in options.testers.split(':')] + except: + print "Error: Unable to parse caches or testers option" sys.exit(1) -for c in cachespec: - if c < 1: - print "Error: Must have 1 or more caches at each level" + if len(cachespec) < 1: + print "Error: Must have at least one level of caches" sys.exit(1) -# Determine the tester multiplier for each level as the string -# elements are per subsystem and it fans out -multiplier = [1] -for c in cachespec: - if c < 1: - print "Error: Must have at least one cache per level" - multiplier.append(multiplier[-1] * c) + if len(cachespec) != len(testerspec) - 1: + print "Error: Testers must have one element more than caches" + sys.exit(1) -numtesters = 0 -for t, m in zip(testerspec, multiplier): - numtesters += t * m + if testerspec[-1] == 0: + print "Error: Must have testers at the uppermost level" + sys.exit(1) -if numtesters > block_size: - print "Error: Number of testers limited to %s because of false sharing" \ - % (block_size) - sys.exit(1) + for t in testerspec: + if t < 0: + print "Error: Cannot have a negative number of testers" + sys.exit(1) + + for c in cachespec: + if c < 1: + print "Error: Must have 1 or more caches at each level" + sys.exit(1) + + if numtesters(cachespec, testerspec) > block_size: + print "Error: Limited to %s testers because of false sharing" \ + % (block_size) + sys.exit(1) # Define a prototype L1 cache that we scale for all successive levels proto_l1 = BaseCache(size = '32kB', assoc = 4, @@ -223,7 +239,7 @@ def make_cache_level(ncaches, prototypes, level, next_cache): # and also make the interval of packet injection longer for the # testers closer to the memory (larger level) to prevent them # hogging all the bandwidth - limit = (len(cachespec) - level + 1) * 10000000 + limit = (len(cachespec) - level + 1) * 100000000 testers = [proto_tester(interval = 10 * (level * level + 1), progress_check = limit) \ for i in xrange(ntesters)] diff --git a/util/memtest-soak.py b/util/memtest-soak.py new file mode 100755 index 000000000..2ece09c21 --- /dev/null +++ b/util/memtest-soak.py @@ -0,0 +1,70 @@ +#! /usr/bin/env python + +# Copyright (c) 2015 ARM Limited +# All rights reserved +# +# The license below extends only to copyright in the software and shall +# not be construed as granting a license to any other intellectual +# property including but not limited to intellectual property relating +# to a hardware implementation of the functionality of the software +# licensed hereunder. You may use the software subject to the license +# terms below provided that you ensure that this notice is replicated +# unmodified and in its entirety in all distributions of the software, +# modified or unmodified, in source code or in binary form. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution; +# neither the name of the copyright holders nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Authors: Andreas Hansson + +import optparse +import subprocess +import sys + +parser = optparse.OptionParser() + +# This script lets the user run a soak test using the false-sharing +# memtest.py example script. It runs a number of iterations with +# random tree topologies generated for each run, and runs for a number +# of ticks. Both the iteration count and the ticks for each run can be +# set on the command line. + +parser.add_option('-c', '--count', type='int', default=100) +parser.add_option('-t', '--ticks', type='int', default=100000000000) + +(options, args) = parser.parse_args() + +if len(args) != 1: + print "Error: Expecting a single argument specifying the gem5 binary" + sys.exit(1) + +gem5_binary = args[0] + +for i in range(options.count): + status = subprocess.call([gem5_binary, 'configs/example/memtest.py', + '-r', '-m %d' % (options.ticks)]) + if status != 0: + print "Error: memtest run failed\n" + sys.exit(1) + +print "memtest soak finished without errors"