diff --git a/src/base/statistics.hh b/src/base/statistics.hh --- a/src/base/statistics.hh +++ b/src/base/statistics.hh @@ -2137,7 +2137,11 @@ Result total() const { return vresult[0]; }; size_type size() const { return 1; } bool scalar() const { return true; } - std::string str() const { return to_string(vresult[0]); } + std::string + str() const + { + return csprintf("ScalarConstant(%s)", to_string(vresult[0])); + } }; template @@ -2166,10 +2170,10 @@ str() const { size_type size = this->size(); - std::string tmp = "("; + std::string tmp = "VectorConstant(("; for (off_type i = 0; i < size; i++) - tmp += csprintf("%s ",to_string(vresult[i])); - tmp += ")"; + tmp += csprintf("%s ", to_string(vresult[i])); + tmp += "))"; return tmp; } }; diff --git a/src/python/SConscript b/src/python/SConscript --- a/src/python/SConscript +++ b/src/python/SConscript @@ -50,6 +50,9 @@ PySource('m5', 'm5/trace.py') PySource('m5.objects', 'm5/objects/__init__.py') PySource('m5.stats', 'm5/stats/__init__.py') +PySource('m5.stats', 'm5/stats/context.py') +PySource('m5.stats', 'm5/stats/display.py') +PySource('m5.stats', 'm5/stats/info.py') PySource('m5.util', 'm5/util/__init__.py') PySource('m5.util', 'm5/util/attrdict.py') PySource('m5.util', 'm5/util/code_formatter.py') @@ -60,6 +63,7 @@ PySource('m5.util', 'm5/util/multidict.py') PySource('m5.util', 'm5/util/orderdict.py') PySource('m5.util', 'm5/util/region.py') +PySource('m5.util', 'm5/util/registry.py') PySource('m5.util', 'm5/util/smartdict.py') PySource('m5.util', 'm5/util/sorteddict.py') PySource('m5.util', 'm5/util/terminal.py') diff --git a/src/python/m5/stats/__init__.py b/src/python/m5/stats/__init__.py --- a/src/python/m5/stats/__init__.py +++ b/src/python/m5/stats/__init__.py @@ -32,7 +32,8 @@ from m5 import internal from m5.internal.stats import schedStatEvent as schedEvent from m5.objects import Root -from m5.util import attrdict, fatal +from m5.stats.context import stat_context +from m5.util import attrdict, fatal, panic, SortedDict outputList = [] def initText(filename, desc=True): @@ -42,10 +43,39 @@ def initSimStats(): internal.stats.initSimStats() +def convert_info(stat): + stat_type = stat.__class__.__name__ + + info = attrdict() + info.type = stat_type + info.name = stat.name + info.desc = stat.desc + info.flags = stat.flags + info.precision = stat.precision + info.prereq = stat.prereq and stat.prereq.id + info.id = stat.id + + if stat_type in ('VectorInfo', 'FormulaInfo', 'VectorDistInfo', + 'Vector2dInfo', 'FormulaInfo'): + info.subnames = [ n for n in stat.subnames ] + info.subdescs = [ n for n in stat.subdescs ] + if stat_type == 'Vector2dInfo': + info.y_subnames = [ n for n in stat.y_subnames ] + info.x = stat.x + info.y = stat.y + if stat_type == 'FormulaInfo': + info.formula = stat.str() + + return info + +class StatsDict(SortedDict): + @staticmethod + def sorted(seq): + return sorted(seq, key=lambda k: k.split('.'), reverse=True) + names = [] -stats_dict = {} -stats_list = [] -raw_stats_list = [] +stats = StatsDict() +sim_stats = [] def enable(): '''Enable the statistics package. Before the statistics package is enabled, all statistics must be created and initialized and once @@ -59,41 +89,166 @@ for cast in __dynamic_cast: val = cast(stat) if val is not None: - stats_list.append(val) - raw_stats_list.append(val) + sim_stats.append(val) break else: fatal("unknown stat type %s", stat) - for stat in stats_list: + for stat in sim_stats: if not stat.check() or not stat.baseCheck(): fatal("stat check failed for '%s' %d\n", stat.name, stat.id) if not (stat.flags & flags.display): stat.name = "__Stat%06d" % stat.id - def less(stat1, stat2): - v1 = stat1.name.split('.') - v2 = stat2.name.split('.') - return v1 < v2 - - stats_list.sort(less) - for stat in stats_list: - stats_dict[stat.name] = stat + for stat in sim_stats: stat.enable() internal.stats.enable(); + for stat in sim_stats: + stats[stat.name] = stat + stat.info = convert_info(stat) + def prepare(): - '''Prepare all stats for data access. This must be done before + '''Prepare all stats for value access. This must be done before dumping and serialization.''' - for stat in stats_list: + for stat in sim_stats: stat.prepare() +def convert_dist(data): + from m5.stats.info import Deviation, Distribution, Histogram + + # Magic numbers correspond to enum DistType in src/base/stats/info.hh + if data.type == 0: + d = Deviation() + elif data.type == 1: + d = Distribution() + elif data.type == 2: + d = Histogram() + else: + panic("Cant deal with dist type '%s'", data.type) + + d.sum = data.sum + d.squares = data.squares + d.samples = data.samples + + if isinstance(d, (Distribution, Histogram)): + d.min = data.min + d.max = data.max + d.bucket_size = data.bucket_size + d.vector = [ v for v in data.cvec ] + + if isinstance(d, Distribution): + d.min_val = data.min_val + d.max_val = data.max_val + d.underflow = data.underflow + d.overflow = data.overflow + + return d + +def convert_sparsehist(data): + from m5.stats.info import SparseHistogramValue + + d = SparseHistogramValue() + + d.samples = data.samples + d.values = {} + + for k in data.cmap.keys(): + d.values[k] = data.cmap[k] + + return d + +def convert_value(stat): + stat_type = stat.__class__.__name__ + if stat_type == 'ScalarInfo': + return stat.result() + if stat_type in ('VectorInfo', 'FormulaInfo'): + return [ v for v in stat.result() ] + if stat_type == 'DistInfo': + return convert_dist(stat.data) + if stat_type == 'VectorDistInfo': + return [ convert_dist(d) for d in stat.data ] + if stat_type == 'Vector2dInfo': + return [ v for v in stat.cvec ] + if stat_type == 'SparseHistInfo': + return convert_sparsehist(stat.data) + return None + +class ValueProxy(object): + def __init__(self, info, value): + self._info = info + self.value = value + + def __getattr__(self, attr): + return getattr(self._info, attr) + + def __nonzero__(self): + return + +def gather_stats(): + prepare() + stats = [] + for stat in sim_stats: + value = convert_value(stat) + info = stat.info + stats.append(ValueProxy(info, value)) + return stats + +def make_context(stats): + from m5.stats import info + info_map = { + 'ScalarInfo' : info.Scalar, + 'VectorInfo' : info.Vector, + 'DistInfo' : info.Dist, + 'VectorDistInfo' : info.VectorDist, + 'Vector2dInfo' : info.Vector2d, + 'FormulaInfo' : info.Formula, + 'SparseHistInfo' : info.SparseHistogram, + } + + ids = {} + for stat in stats: + ids[stat.id] = stat + + context = stat_context() + cids = {} + for stat in stats: + cls = info_map[stat.type] + s = cls(stat.id, ids, context) + context.insert(s.name, s) + cids[s.id] = s + + for name,stat in context.iterate(): + if stat.prereq is not None: + stat.prereq = cids[stat.prereq] + + return context + +def shelve(stats, filename): + import pickle, shelve + shelf = shelve.open(filename, protocol=pickle.HIGHEST_PROTOCOL, + writeback=True) + shelf['allstats'] = [ stat.id for stat in stats ] + for stat in stats: + shelf['info%d' % stat.id] = stat._info + shelf['value%d' % stat.id] = stat.value + +def display_text(context): + from m5.stats.display import display + + print + print "---------- Begin Simulation Statistics ----------" + for name,stat in context.iterate(): + display(stat) + print + print "---------- End Simulation Statistics ----------" + lastDump = 0 def dump(): - '''Dump all statistics data to the registered outputs''' + '''Dump all statistics values to the registered outputs''' curTick = m5.curTick() @@ -103,14 +258,9 @@ return lastDump = curTick - prepare() - - for output in outputList: - if output.valid(): - output.begin() - for stat in stats_list: - output.visit(stat) - output.end() + stats = gather_stats() + context = make_context(stats) + display_text(context) def reset(): '''Reset all statistics to the base state''' @@ -121,7 +271,7 @@ for obj in root.descendants(): obj.resetStats() # call any other registered stats reset callbacks - for stat in stats_list: + for stat in internal.stats.statsList(): stat.reset() internal.stats.processResetQueue() diff --git a/src/python/m5/stats/context.py b/src/python/m5/stats/context.py --- /dev/null +++ b/src/python/m5/stats/context.py @@ -0,0 +1,93 @@ +# Copyright (c) 2010 The Hewlett-Packard Development Company +# All rights reserved. +# +# 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: Nathan Binkert + +class stat_context_iter(object): + def __init__(self, context): + self.items = [ ('', iter(sorted(context.iteritems()))) ] + + def __iter__(self): + return self + + def next(self): + while True: + if not self.items: + raise StopIteration + + base,item = self.items[0] + + try: + k,v = item.next() + except StopIteration: + self.items.pop(0) + continue + + k = base + k + if isinstance(v, stat_context): + self.items[0:0] = [ (k + '.', iter(sorted(v.iteritems()))) ] + else: + return k,v + +class stat_context(dict): + def __getattr__(self, attr): + try: + return self[attr] + except KeyError: + raise AttributeError, \ + "'%s' object has no attribute '%s'" % (type(self), attr) + + def insert(self, key, value): + path = key.split('.') + + level = self + while len(path) > 1: + name = path.pop(0) + try: + level = level[name] + except KeyError: + context = stat_context() + level[name] = context + level = context + + name = path.pop(0) + level[name] = value + + def iterate(self): + return stat_context_iter(self) + + def find(self, name): + path = name.split('.') + + node = self + while path: + item = path.pop(0) + try: + node = node[item] + except KeyError: + raise KeyError, name + + return node diff --git a/src/python/m5/stats/display.py b/src/python/m5/stats/display.py --- /dev/null +++ b/src/python/m5/stats/display.py @@ -0,0 +1,367 @@ +# Copyright (c) 2003-2004 The Regents of The University of Michigan +# All rights reserved. +# +# 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: Nathan Binkert + +from math import isnan, sqrt + +from m5.stats import flags +from m5.stats.info import * +from m5.util.registry import registry + +all = False +descriptions = True + +class Value(object): + def __init__(self, value, precision, percent = False): + self.value = float(value) + self.precision = precision + self.percent = percent + def __str__(self): + if self.precision >= 0: + format = "%%.%df" % self.precision + elif self.value == 0.0: + format = "%.0f" + elif self.value % 1.0 == 0.0: + format = "%.0f" + else: + format = "%f" + value = self.value + if self.percent: + value = value * 100.0 + value = format % value + + if self.percent: + value = value + "%" + + return value + +class DisplayBase(object): + def __init__(self, stat): + self._stat = stat + self.reset() + + def __getattr__(self, attr): + return getattr(self._stat, attr) + + def reset(self): + pass + +class Display(DisplayBase): + def reset(self): + self.pdf = float('nan') + self.cdf = float('nan') + + def update(self, val, total): + self.value = val + if total: + pdf = val / total + self.pdf = pdf + self.cdf += pdf + + def __str__(self): + do_desc = descriptions and self.desc + + value = Value(self.value, self.precision) + output = "%-40s %12s" % (self.name, value) + + if not isnan(self.pdf): + pdf = Value(self.pdf, 2, True) + output += " %10s" % pdf + elif do_desc: + output += ' ' * 11 + + if not isnan(self.cdf): + cdf = Value(self.cdf, 2, True) + output += " %10s" % cdf + elif do_desc: + output += ' ' * 11 + + if do_desc: + output += " # %s" % self.desc + + return output + + def dodisplay(self): + if all: + return True + if self.prereq is not None and not self.prereq: + return False + if self.value == 0.0 and (self.flags & flags.nozero): + return False + if isnan(self.value) and (self.flags & flags.nonan): + return False + return (self.flags & flags.display) + + def display(self, out): + if self.dodisplay(): + print >>out, self + +class VectorDisplay(DisplayBase): + def display(self, out): + if not self.value: + return + + p = Display(self) + p.flags = self.flags + p.precision = self.precision + + if not isinstance(self.value, (list, tuple)): + p.name = self.name + p.desc = self.desc + p.value = self.value + p.display(out) + return + + mytotal = reduce(lambda x,y: float(x) + float(y), self.value) + mycdf = 0.0 + + value = self.value + if any(self.subnames): + subnames = [''] * len(value) + for i,subname in enumerate(self.subnames): + if subname: + subnames[i] = '::%s' % subname + else: + subnames = [ '::%d' % i for i in range(len(value)) ] + + subdescs = [self.desc] * len(value) + if self.subdescs: + for i in xrange(min(len(value), len(self.subdescs))): + if self.subdescs[i]: + subdescs[i] = self.subdescs[i] + + for val,sname,sdesc in map(None, value, subnames, subdescs): + if mytotal > 0.0: + mypdf = float(val) / float(mytotal) + mycdf += mypdf + if (self.flags & flags.pdf): + p.pdf = mypdf + p.cdf = mycdf + + if not sname: + continue + + p.name = self.name + sname + p.desc = sdesc + p.value = val + p.display(out) + + if self.flags & flags.total: + p.reset() + p.name = self.name + '::total' + p.desc = self.desc + p.value = mytotal + p.display(out) + +class DistDisplay(DisplayBase): + def display(self, out): + v = self.value + + p = Display(self) + + p.name = "%s::samples" % self.name + p.value = v.samples + p.display(out) + + p.name = "%s::mean" % self.name + p.value = v.mean() + p.display(out) + + p.name = "%s::stdev" % self.name + p.value = v.stdev() + p.display(out) + + if type(v) == Deviation: + return + + total = sum(v.vector) + if type(v) == Distribution: + if not isnan(v.underflow): + total += v.underflow + if not isnan(v.overflow): + total += v.overflow + + if total: + p.pdf = 0.0 + p.cdf = 0.0 + + if type(v) == Distribution and not isnan(v.underflow): + p.name = "%s::underflows" % self.name + p.update(v.underflow, total) + p.display(out) + + for i in xrange(len(v.vector)): + low = i * v.bucket_size + v.min + high = min(low + v.bucket_size - 1.0, v.max) + name = "%s::%d" % (self.name, low) + if low < high: + name = "%s-%d" % (name, high) + + p.name = name + p.update(v.vector[i], total) + p.display(out) + + if type(v) == Distribution and not isnan(v.overflow): + p.name = "%s::overflows" % self.name + p.update(v.overflow, total) + p.display(out) + + p.reset() + + if type(v) == Distribution and not isnan(v.min_val): + p.name = "%s::min_value" % self.name + p.value = v.min_val + p.display(out) + + if type(v) == Distribution and not isnan(v.max_val): + p.name = "%s::max_value" % self.name + p.value = v.max_val + p.display(out) + + p.name = "%s::total" % self.name + p.value = total + p.display(out) + +class SparseHistDisplay(DisplayBase): + def display(self, out): + v = self.value + p = Display(self) + + p.name = "%s::samples" % self.name + p.desc = self.desc + p.value = v.samples + p.display(out) + + for x in sorted(v.values.iterkeys()): + p.reset() + p.name = "%s::%d" % (self.name, x) + p.value = v.values[x] + p.display(out) + + +register_display = registry() + +@register_display(Scalar) +def display_scalar(stat, out): + p = Display(stat) + p.value = value(stat) + p.display(out) + +@register_display(Vector) +def display_vector(stat, out): + if len(stat) == 1: + p = Display(stat) + p.value = value(stat, 0) + else: + p = VectorDisplay(stat) + p.value = [ value(stat, i) for i in xrange(len(stat)) ] + p.display(out) + +@register_display(Formula) +def display_formula(stat, out): + if scalar(stat): + display_scalar(stat, out) + elif vector(stat): + display_vector(stat, out) + +@register_display(Dist) +def dist_display(stat, out): + p = DistDisplay(stat) + p.display(out) + +@register_display(SparseHistogram) +def sparsehist_display(stat, out): + p = SparseHistDisplay(stat) + p.display(out) + +@register_display(VectorDist) +def vectordist_display(stat, out): + for i,value in enumerate(stat.value): + subnames = stat.subnames[i] + subdescs = stat.subdescs[i] + + p = DistDisplay(stat) + p.value = value + if subnames: + p.name = '%s::%s' % (stat.name, subnames) + else: + p.name = '%s_%d' % (stat.name, i) + + p.desc = subdescs or stat.desc + + p.display(out) + + if (stat.flags & flags.total): + p = DistDisplay(stat) + p.name = '%s.total' % (stat.name) + + p.value = stat.value[0].copy() + for dist in stat.value[1:]: + p.value += dist + + p.display(out) + +@register_display(Vector2d) +def vector2d_display(stat, out): + p = VectorDisplay(stat) + + if any(stat.subnames): + subnames = stat.subnames + else: + subnames = range(stat.x) + + if any(stat.y_subnames): + y_subnames = stat.y_subnames + else: + y_subnames = range(stat.y) + + for x,sname in enumerate(subnames): + o = x * stat.y + p.value = stat.value[o:o+stat.y] + p.name = '%s_%s' % (stat.name, sname) + p.subnames = y_subnames + p.display(out) + + if stat.flags & flags.total: + p.name = '%s_total' % (stat.name, ) + p.subnames = y_subnames + p.value = [] + for y in range(stat.y): + total = sum(stat.value[x * stat.y + y] for x in xrange(stat.x)) + p.value.append(total) + + p.display(out) + +def display(stat, out=None): + if out is None: + import sys + out = sys.stdout + + func = register_display[type(stat)] + func(stat, out) + +__all__ = [ 'display' ] diff --git a/src/python/m5/stats/info.py b/src/python/m5/stats/info.py --- /dev/null +++ b/src/python/m5/stats/info.py @@ -0,0 +1,657 @@ +# Copyright (c) 2003-2004 The Regents of The University of Michigan +# All rights reserved. +# +# 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: Nathan Binkert + +import math, operator, re, types + +from m5.stats import flags + +class ProxyError(Exception): + pass + +def unproxy(proxy): + if hasattr(proxy, '__unproxy__'): + return proxy.__unproxy__() + + return proxy + +def scalar(stat): + stat = unproxy(stat) + assert(stat.__scalar__() != stat.__vector__()) + return stat.__scalar__() + +def vector(stat): + stat = unproxy(stat) + assert(stat.__scalar__() != stat.__vector__()) + return stat.__vector__() + +def value(stat, *args): + stat = unproxy(stat) + return stat.__value__(*args) + +def values(stat): + stat = unproxy(stat) + result = [] + for i in xrange(len(stat)): + val = value(stat, i) + result.append(val) + return result + +def total(stat): + return sum(values(stat)) + +def len(stat): + stat = unproxy(stat) + return stat.__len__() + +class Value(object): + def __getattr__(self, attr): + if attr in ('__scalar__', '__vector__'): + raise AttributeError, "must define %s for %s" % (attr, type (self)) + + raise AttributeError, \ + "'%s' object has no attribute '%s'" % (type(self), attr) + + def __add__(self, other): + return BinaryProxy(operator.__add__, self, other) + def __sub__(self, other): + return BinaryProxy(operator.__sub__, self, other) + def __mul__(self, other): + return BinaryProxy(operator.__mul__, self, other) + def __div__(self, other): + return BinaryProxy(operator.__div__, self, other) + def __truediv__(self, other): + return BinaryProxy(operator.__truediv__, self, other) + def __floordiv__(self, other): + return BinaryProxy(operator.__floordiv__, self, other) + + def __radd__(self, other): + return BinaryProxy(operator.__add__, other, self) + def __rsub__(self, other): + return BinaryProxy(operator.__sub__, other, self) + def __rmul__(self, other): + return BinaryProxy(operator.__mul__, other, self) + def __rdiv__(self, other): + return BinaryProxy(operator.__div__, other, self) + def __rtruediv__(self, other): + return BinaryProxy(operator.__truediv__, other, self) + def __rfloordiv__(self, other): + return BinaryProxy(operator.__floordiv__, other, self) + + def __neg__(self): + return UnaryProxy(operator.__neg__, self) + def __pos__(self): + return UnaryProxy(operator.__pos__, self) + def __abs__(self): + return UnaryProxy(operator.__abs__, self) + +class ScalarValue(Value): + def __scalar__(self): + return True + + def __vector__(self): + return False + + def __value__(self): + raise AttributeError, '__value__ must be defined' + +class VectorItemProxy(Value): + def __init__(self, proxy, index): + self.proxy = proxy + self.index = index + + def __scalar__(self): + return True + + def __vector__(self): + return False + + def __value__(self): + return value(self.proxy, self.index) + +class VectorValue(Value): + def __scalar__(self): + return False + + def __vector__(self): + return True + + def __value__(self, index): + raise AttributeError, '__value__ must be defined' + + def __getitem__(self, index): + return VectorItemProxy(self, index) + +class ScalarConstant(ScalarValue): + def __init__(self, constant): + self.constant = constant + def __value__(self): + return self.constant + def __str__(self): + return str(self.constant) + +class VectorConstant(VectorValue): + def __init__(self, constant): + self.constant = constant + def __value__(self, index): + return self.constant[index] + def __len__(self): + return len(self.constant) + def __str__(self): + return str(self.constant) + +def WrapValue(value): + if isinstance(value, (int, long, float)): + return ScalarConstant(value) + if isinstance(value, (list, tuple)): + return VectorConstant(value) + if isinstance(value, Value): + return value + + raise AttributeError, 'Only values can be wrapped' + +class Statistic(object): + def __init__(self, id, db, context): + self.__dict__['id'] = id + self.__dict__['db'] = db + self.__dict__['context'] = context + + @property + def stat(self): + return self.db[self.id] + + def __getattr__(self, attr): + return getattr(self.stat, attr) + + def __setattr__(self, attr, value): + if attr in ('id', 'db', 'context', 'info', 'value'): + raise AttributeError, '%s is read only' % attr + + super(Statistic, self).__setattr__(attr, value) + + def __str__(self): + return self.name + +class ValueProxy(Value): + def __getattr__(self, attr): + if attr == '__value__': + if scalar(self): + return self.__scalarvalue__ + if vector(self): + return self.__vectorvalue__ + if attr == '__len__': + if vector(self): + return self.__vectorlen__ + return super(ValueProxy, self).__getattribute__(attr) + +class UnaryProxy(ValueProxy): + def __init__(self, op, arg): + self.op = op + self.arg = WrapValue(arg) + + def __scalar__(self): + return scalar(self.arg) + + def __vector__(self): + return vector(self.arg) + + def __scalarvalue__(self): + val = value(self.arg) + return self.op(val) + + def __vectorvalue__(self, index): + val = value(self.arg, index) + return self.op(val) + + def __vectorlen__(self): + return len(unproxy(self.arg)) + + def __str__(self): + if self.op == operator.__neg__: + return '-%s' % str(self.arg) + if self.op == operator.__pos__: + return '+%s' % str(self.arg) + if self.op == operator.__abs__: + return 'abs(%s)' % self.arg + +class BinaryProxy(ValueProxy): + def __init__(self, op, arg0, arg1): + super(BinaryProxy, self).__init__() + self.op = op + self.arg0 = WrapValue(arg0) + self.arg1 = WrapValue(arg1) + + def __scalar__(self): + return scalar(self.arg0) and scalar(self.arg1) + + def __vector__(self): + return vector(self.arg0) or vector(self.arg1) + + def __scalarvalue__(self): + val0 = value(self.arg0) + val1 = value(self.arg1) + try: + return self.op(val0, val1) + except ZeroDivisionError: + return float('nan') + + def __vectorvalue__(self, index): + if scalar(self.arg0): + val0 = value(self.arg0) + if vector(self.arg0): + val0 = value(self.arg0, index) + if scalar(self.arg1): + val1 = value(self.arg1) + if vector(self.arg1): + val1 = value(self.arg1, index) + + try: + return self.op(val0, val1) + except ZeroDivisionError: + if val0 != 0 and val1 == 0: + return float('inf') + return float('nan') + + def __vectorlen__(self): + if vector(self.arg0) and scalar(self.arg1): + return len(self.arg0) + if scalar(self.arg0) and vector(self.arg1): + return len(self.arg1) + + len0 = len(self.arg0) + len1 = len(self.arg1) + + if len0 != len1: + raise AttributeError, \ + "vectors of different lengths %d != %d '%s'" % \ + (len0, len1, self) + + return len0 + + def __str__(self): + ops = { operator.__add__ : '+', + operator.__sub__ : '-', + operator.__mul__ : '*', + operator.__div__ : '/', + operator.__truediv__ : '/', + operator.__floordiv__ : '//' } + + return '(%s %s %s)' % (str(self.arg0), ops[self.op], str(self.arg1)) + +class Proxy(Value): + def __init__(self, name, dict): + self.name = name + self.dict = dict + + def __unproxy__(self): + return unproxy(self.dict[self.name]) + + def __getitem__(self, index): + return ItemProxy(self, index) + + def __getattr__(self, attr): + return AttrProxy(self, attr) + + def __str__(self): + return str(self.dict[self.name]) + +class ItemProxy(Proxy): + def __init__(self, proxy, index): + self.proxy = proxy + self.index = index + + def __unproxy__(self): + return unproxy(unproxy(self.proxy)[self.index]) + + def __str__(self): + return '%s[%s]' % (self.proxy, self.index) + +class AttrProxy(Proxy): + def __init__(self, proxy, attr): + self.proxy = proxy + self.attr = attr + + def __unproxy__(self): + proxy = unproxy(self.proxy) + try: + attr = getattr(proxy, self.attr) + except AttributeError, e: + raise ProxyError, e + return unproxy(attr) + + def __str__(self): + return '%s.%s' % (self.proxy, self.attr) + +class ProxyGroup(object): + def __init__(self, dict=None, **kwargs): + self.__dict__['dict'] = {} + + if dict is not None: + self.dict.update(dict) + + if kwargs: + self.dict.update(kwargs) + + def __getattr__(self, name): + return Proxy(name, self.dict) + + def __setattr__(self, attr, value): + self.dict[attr] = value + +class Scalar(Statistic,ScalarValue): + def __value__(self): + return self.value + + def __nonzero__(self): + return self.value != 0 + +class Vector(Statistic,VectorValue): + def __value__(self, item): + return self.value[item] + + def __len__(self): + return len(self.value) + + def __nonzero__(self): + return any(self.value) + +class Formula(Statistic,Value): + def __getattr__(self, attr): + if attr == 'formula': + formula = super(Formula, self).__getattr__(attr) + self.formula = re.sub(':', '__', formula) + return self.formula + + if attr == '_the_value': + g = dict(total=total, + ScalarConstant=ScalarConstant, + VectorConstant=VectorConstant) + self._the_value = eval(self.formula, g, self.context) + return self._the_value + + if attr in ('__scalar__', '__vector__', '__value__', '__len__'): + return getattr(self._the_value, attr) + + return super(Formula, self).__getattr__(attr) + + def __nonzero__(self): + return any(self.value) + +class Deviation(object): + def clear(self): + self.samples = 0 + self.sum = 0 + self.squares = 0 + + def copy(self): + cls = type(self) + copy = cls() + copy.samples = self.samples + copy.sum = self.sum + copy.squares = self.squares + return copy + + def mean(self): + if self.samples == 0: + return float('nan') + + return self.sum / self.samples + + def stdev(self): + num = (self.samples * self.squares - self.sum ** 2) + den = (self.samples * (self.samples - 1)) + + if den == 0: + return float('nan') + + return math.sqrt(num / den) + + def comparable(self, other): + return type(self) == type(other) + + def __nonzero__(self): + return bool(self.samples) + + def __eq__(self, other): + if not self.comparable(other): + return False + + return (self.samples == other.samples and + self.sum == other.sum and + self.squares == other.squares) + + def __iadd__(self, other): + assert self.comparable(other) + + self.samples += other.samples + self.sum += other.sum + self.squares += other.squares + return self + + def __sub__(self, other): + assert self.comparable(other) + + self.samples -= other.samples + self.sum -= other.sum + self.squares -= other.squares + return self + + def __itruediv__(self, other): + if not other: + return self + self.samples /= other + self.sum /= other + self.squares /= other + return self + +class Histogram(Deviation): + def clear(self): + super(Histogram, self).clear() + + def copy(self): + copy = super(Histogram, self).copy() + copy.min = self.min + copy.max = self.max + copy.bucket_size = self.bucket_size + copy.vector = self.vector[:] + return copy + + def comparable(self, other): + if not super(Histogram, self).comparable(other): + return False + + return (len(self.vector) == len(other.vector) and + self.min == other.min and + self.max == other.max and + self.bucket_size == other.bucket_size) + + def __iadd__(self, other): + super(Histogram, self).__iadd__(other) + self.vector = map(operator.__add__, self.vector, other.vector) + return self + + def __isub__(self, other): + super(Histogram, self).__isub__(other) + self.vector = map(operator.__sub__, self.vector, other.vector) + return self + + def __itruediv__(self, other): + super(Histogram, self).__itruediv__(other) + self.vector = map(lambda x: x / other, self.vector) + return self + +class Distribution(Histogram): + def clear(self): + super(Distribution, self).clear() + self.min_val = float('Inf') + self.max_val = -float('Inf') + self.underflow = 0.0 + self.overflow = 0.0 + + def copy(self): + copy = super(Distribution, self).copy() + copy.min_val = self.min_val + copy.max_val = self.max_val + copy.underflow = self.underflow + copy.overflow = self.overflow + return copy + + def __iadd__(self, other): + super(Distribution, self).__iadd__(other) + self.min_val = min(self.min_val, other.min_val) + self.max_val = min(self.max_val, other.max_val) + self.underflow += other.underflow + self.overflow += other.overflow + return self + + def __isub__(self, other): + super(Distribution, self).__isub__(other) + self.min_val = min(self.min_val, other.min_val) + self.max_val = min(self.max_val, other.max_val) + self.underflow -= other.underflow + self.overflow -= other.overflow + return self + + def __itruediv__(self, other): + super(Distribution, self).__itruediv__(other) + self.underflow /= other + self.overflow /= other + return self + +class SparseHistogramValue(Deviation): + def copy(self): + copy = super(SparseHistogramValue, self).copy() + copy.values = self.values + return copy + + def clear(self): + super(SparseHistogramValue, self).clear() + self.values = {} + +class SparseHistogram(Statistic): + def comparable(self, other): + return self.name == other.name and \ + self.value.compareable(other.value) + + def __nonzero__(self): + return bool(self.value) + + def __eq__(self, other): + return self.value == other.value + + def __value__(self, item): + return self.value[item] + +class Dist(Statistic): + def comparable(self, other): + return self.name == other.name and \ + self.value.compareable(other.value) + + def __nonzero__(self): + return bool(self.value) + + def __eq__(self, other): + return self.value == other.value + + def __isub__(self, other): + self.value -= other.value + return self + + def __iadd__(self, other): + self.value += other.value + return self + + def __itruediv__(self, other): + if not other: + return self + self.value /= other + return self + +class VectorDist(Statistic): + def comparable(self, other): + return self.name == other.name and \ + all(map(lambda x, y : x.comparable(y), self.value, other.value)) + + def __nonzero__(self): + return any(self.value) + + def __eq__(self, other): + return all(map(lambda x, y : x == y, self.value, other.value)) + + def __isub__(self, other): + if isinstance(self.value, (list, tuple)) and \ + isinstance(other.value, (list, tuple)): + for sd,od in zip(self.value, other.value): + sd -= od + else: + self.value -= other.value + return self + + def __iadd__(self, other): + if isinstance(self.value, (list, tuple)) and \ + isinstance(other.value, (list, tuple)): + for sd,od in zip(self.value, other.value): + sd += od + else: + self.value += other.value + return self + + def __itruediv__(self, other): + if not other: + return self + if isinstance(self.value, (list, tuple)): + for iq in xrange(len(self.value)): + self.value[i] /= other + else: + self.value /= other + return self + +class Vector2d(Statistic): + def comparable(self, other): + return self.name == other.name and self.x == other.x and \ + self.y == other.y + + def __nonzero__(self): + return any(self.value) + + def __eq__(self, other): + return True + + def __isub__(self, other): + return self + + def __iadd__(self, other): + return self + + def __itruediv__(self, other): + if not other: + return self + return self + +__all__ = [ 'unproxy', 'scalar', 'vector', 'value', 'values', 'total', 'len', + 'Scalar', 'Vector', 'Formula', 'Deviation', 'Distribution', + 'Histogram', 'SparseHistogram', 'Dist', 'VectorDist', 'Vector2d' ] diff --git a/src/python/m5/util/registry.py b/src/python/m5/util/registry.py --- /dev/null +++ b/src/python/m5/util/registry.py @@ -0,0 +1,42 @@ +# Copyright (c) 2006 Nathan Binkert +# All rights reserved. +# +# 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. + +class register(object): + def __init__(self, registry, type): + self.registry = registry + self.type = type + + def __call__(self, f): + self.registry[self.type] = f + return f + +class registry(dict): + def __call__(self, type): + if type in self: + raise AttributeError, "%s already found in registry" + self[type] = None + return register(self, type) + diff --git a/src/python/swig/stats.i b/src/python/swig/stats.i --- a/src/python/swig/stats.i +++ b/src/python/swig/stats.i @@ -33,6 +33,7 @@ %include %include %include +%include %include %{ @@ -125,6 +126,7 @@ %template(vector_double) vector; %template(vector_string) vector; %template(vector_DistData) vector; +%template(map_double_int) map; } namespace Stats {