Author: Anton Gulenko <anton.gule...@googlemail.com>
Branch: storage
Changeset: r715:0c8b9379f9d5
Date: 2014-03-28 14:01 +0100
http://bitbucket.org/pypy/lang-smalltalk/changeset/0c8b9379f9d5/

Log:    Moved storage-statistics to own module, integrated with storage code
        and main target. Added test. Added safety-assert to prevent storage-
        access during bootstrapping phase (specialized storage like
        ClassShadows cannot be used before the objects have been filled in).
        Made store_w_superclass and store_w_methoddict of ClassShadow
        slightly more consistent.

diff --git a/spyvm/model.py b/spyvm/model.py
--- a/spyvm/model.py
+++ b/spyvm/model.py
@@ -15,7 +15,7 @@
 that create W_PointersObjects of correct size with attached shadows.
 """
 import sys, weakref
-from spyvm import constants, error, version
+from spyvm import constants, error, version, storage_statistics
 from spyvm.version import elidable_for_version
 
 from rpython.rlib import rrandom, objectmodel, jit, signature
@@ -24,7 +24,6 @@
 from rpython.tool.pairtype import extendabletype
 from rpython.rlib.objectmodel import instantiate, compute_hash, 
import_from_mixin, we_are_translated
 from rpython.rtyper.lltypesystem import lltype, rffi
-from rpython.rlib.listsort import TimSort
 from rsdl import RSDL, RSDL_helper
 
 class W_Object(object):
@@ -468,6 +467,7 @@
     Float)."""
     _attrs_ = ['w_class']
     repr_classname = "W_AbstractObjectWithClassReference"
+    w_class = None
     
     def __init__(self, space, w_class):
         if w_class is not None:     # it's None only for testing and space 
generation
@@ -490,10 +490,12 @@
 
     def guess_classname(self):
         if self.has_class():
-            class_shadow = self.class_shadow(self.w_class.space())
-            # Three question marks, because it would be highly irregular to 
have
-            # an initialized ClassShadow without an initialized name field.
-            return class_shadow.name or "???"
+            if self.w_class.has_shadow():
+                class_shadow = self.class_shadow(self.w_class.space())
+                return class_shadow.name
+            else:
+                # We cannot access the class during the initialization 
sequence.
+                return "?? (class not initialized)"
         else:
             return "? (no class)"
     
@@ -517,50 +519,12 @@
         assert w_class is not None
         return w_class.as_class_get_shadow(space)
 
-class StatsSorter(TimSort):
-    def lt(self, a, b):
-        if a[0] == b[0]:
-            if a[1] == b[1]:
-                return a[2] < b[2]
-            else:
-                return a[1] < b[1]
-        else:
-            return a[0] < b[0]
-class StrategyStatistics(object):
-    # Key: (operation_name, old_strategy, new_strategy)
-    # Value: [sizes]
-    stats = {}
-    do_log = False
-    do_stats = False
-    do_stats_sizes = False
-    
-    def stat_operation(self, operation_name, old_strategy, new_strategy, size):
-        key = (operation_name, old_strategy, new_strategy)
-        if not key in self.stats:
-            self.stats[key] = []
-        self.stats[key].append(size)
-    def log_operation(self, op, new_strategy_tag, old_strategy_tag, classname, 
size):
-        print "%s (%s, was %s) of %s size %d" % (op, new_strategy_tag, 
old_strategy_tag, classname, size)
-    def sorted_keys(self):
-        keys = [ x for x in self.stats ]
-        StatsSorter(keys).sort()
-        return keys
-    def print_stats(self):
-        for key in self.sorted_keys():
-            sizes = self.stats[key]
-            sum = 0
-            for s in sizes:
-                sum += s
-            print "%s: %d times, avg size: %d" % (key, len(sizes), 
sum/len(sizes))
-            if self.do_stats_sizes:
-                print "       All sizes: %s" % sizes
-strategy_stats = StrategyStatistics()
-
 class W_AbstractPointersObject(W_AbstractObjectWithClassReference):
     """Common object."""
     _attrs_ = ['shadow']
     shadow = None
     repr_classname = "W_AbstractPointersObject"
+    log_storage = storage_statistics.log
     
     @jit.unroll_safe
     def __init__(self, space, w_class, size):
@@ -570,6 +534,7 @@
         
     def initialize_storage(self, space, size):
         self.store_shadow(self.empty_storage(space, size))
+        self.log_storage("Initialized")
         
     def fillin(self, space, g_self):
         W_AbstractObjectWithClassReference.fillin(self, space, g_self)
@@ -579,17 +544,28 @@
         pointers = g_self.get_pointers()
         self.store_shadow(self.storage_for_list(space, pointers))
         self.store_all(space, pointers)
+        self.log_storage("Filledin", log_classname=False)
         
     def empty_storage(self, space, size):
         raise NotImplementedError()
     def storage_for_list(self, space, vars):
         raise NotImplementedError()
     
+    def assert_shadow(self):
+        # Failing the following assert most likely indicates a bug. The shadow 
can only be absent during
+        # the bootstrapping sequence. It will be initialized in the fillin() 
method. Before that, it should
+        # not be switched to a specialized shadow, and the space is also not 
yet available here! Otherwise,
+        # the specialized shadow will attempt to read information from an 
uninitialized object.
+        shadow = self.shadow
+        assert shadow, "The shadow has not been initialized yet!"
+        return shadow
+    
     def switch_shadow(self, new_shadow):
-        if self.shadow is not None:
-            new_shadow.copy_from(self.shadow)
+        old_shadow = self.assert_shadow()
+        new_shadow.copy_from(old_shadow)
         self.store_shadow(new_shadow)
         new_shadow.attach_shadow()
+        self.log_storage("Switched", old_shadow)
     
     def store_with_new_storage(self, new_storage, n0, w_val):
         space = self.space()
@@ -597,8 +573,7 @@
         self.store(space, n0, w_val)
     
     def space(self):
-        assert self.shadow, "Cannot access space without a shadow!"
-        return self.shadow.space
+        return self.assert_shadow().space
         
     def __str__(self):
         if self.has_shadow() and self.shadow.provides_getname:
diff --git a/spyvm/shadow.py b/spyvm/shadow.py
--- a/spyvm/shadow.py
+++ b/spyvm/shadow.py
@@ -263,8 +263,8 @@
 
     _attrs_ = ["name", "_instance_size", "instance_varsized", "instance_kind",
                 "_s_methoddict", "_s_superclass", "subclass_s"]
-    name = '??'
-    _s_superclass = None
+    name = '??? (incomplete class info)'
+    _s_superclass = _s_methoddict = None
     provides_getname = True
     repr_classname = "ClassShadow"
     
@@ -277,10 +277,7 @@
         if n0 == constants.CLASS_SUPERCLASS_INDEX:
             self.store_w_superclass(w_val)
         elif n0 == constants.CLASS_METHODDICT_INDEX:
-            assert isinstance(w_val, model.W_PointersObject)
-            if not w_val.is_same_object(self.space.w_nil):
-                self._s_methoddict = w_val.as_methoddict_get_shadow(self.space)
-                self._s_methoddict.s_class = self
+            self.store_w_methoddict(w_val)
         elif n0 == constants.CLASS_FORMAT_INDEX:
             # read and painfully decode the format
             assert isinstance(w_val, model.W_SmallInteger)
@@ -340,18 +337,33 @@
         self.changed()
     
     def store_w_superclass(self, w_class):
+        superclass = self._s_superclass
         if w_class is None or w_class.is_same_object(self.space.w_nil):
+            if superclass: superclass.detach_s_class(self)
             self._s_superclass = None
         else:
             assert isinstance(w_class, model.W_PointersObject)
-            s_scls = w_class.as_class_get_shadow(self.space)
-            if self._s_superclass is s_scls:
+            s_new_superclass = w_class.as_class_get_shadow(self.space)
+            if superclass is s_new_superclass:
                 return
-            if self._s_superclass is not None:
-                self._s_superclass.detach_s_class(self)
-            self._s_superclass = s_scls
-            self._s_superclass.attach_s_class(self)
+            if superclass: superclass.detach_s_class(self)
+            self._s_superclass = s_new_superclass
+            s_new_superclass.attach_s_class(self)
 
+    def store_w_methoddict(self, w_methoddict):
+        methoddict = self._s_methoddict
+        if w_methoddict is None or 
w_methoddict.is_same_object(self.space.w_nil):
+            if methoddict: methoddict.s_class = None
+            self._s_methoddict = None
+        else:
+            assert isinstance(w_methoddict, model.W_PointersObject)
+            s_new_methoddict = 
w_methoddict.as_methoddict_get_shadow(self.space)
+            if methoddict is s_new_methoddict:
+                return
+            if methoddict: methoddict.s_class = None
+            self._s_methoddict = s_new_methoddict
+            self._s_methoddict.s_class = self
+            
     def attach_s_class(self, s_other):
         self.subclass_s[s_other] = None
 
@@ -406,7 +418,7 @@
         return self._s_superclass
 
     def getname(self):
-        return self.name or '?'
+        return self.name
 
     # _______________________________________________________________
     # Methods for querying the format word, taken from the blue book:
diff --git a/spyvm/storage_statistics.py b/spyvm/storage_statistics.py
new file mode 100644
--- /dev/null
+++ b/spyvm/storage_statistics.py
@@ -0,0 +1,92 @@
+
+from rpython.rlib.listsort import TimSort
+
+class StatsSorter(TimSort):
+    """Sort a tuple of 3 strings"""
+    def lt(self, a, b):
+        if a[0] == b[0]:
+            if a[1] == b[1]:
+                return a[2] < b[2]
+            else:
+                return a[1] < b[1]
+        else:
+            return a[0] < b[0]
+
+class StorageStatistics(object):
+    # Key: (operation_name, old_storage, new_storage)
+    # Value: [sizes]
+    stats = {}
+    
+    do_log = False
+    do_stats = False
+    do_stats_sizes = False
+    
+    def log(self, w_obj, operation, old_storage_object, log_classname):
+        if self.do_log or self.do_stats:
+            new_storage = w_obj.shadow.repr_classname
+            if old_storage_object:
+                old_storage = old_storage_object.repr_classname
+            else:
+                old_storage = None
+            size = w_obj.size()
+            
+            key = self.make_key(operation, old_storage, new_storage)
+            if _stats.do_stats:
+                self.stat_operation(key, size)
+            if self.do_log:
+                if log_classname:
+                    classname = w_obj.guess_classname()
+                else:
+                    classname = None
+                self.log_operation(key, size, classname)
+    
+    def make_key(self, operation, old_storage, new_storage):
+        return (operation, old_storage, new_storage)
+    
+    def stat_operation(self, key, size):
+        if not key in self.stats:
+            self.stats[key] = []
+        self.stats[key].append(size)
+
+    def log_operation(self, key, size, classname):
+        print self.log_operation_string(key, size, classname)
+        
+    def key_string(self, key):
+        if key[1]:
+            return "%s (%s -> %s)" % (key[0], key[1], key[2])
+        else:
+            return "%s (%s)" % (key[0], key[2])
+        
+    def log_operation_string(self, key, size, classname):
+        if classname:
+            return "%s of %s size %d" % (self.key_string(key), classname, size)
+        else:
+            return "%s size %d" % (self.key_string(key), size)
+        
+    def sorted_keys(self):
+        keys = [ x for x in self.stats ]
+        StatsSorter(keys).sort()
+        return keys
+        
+    def print_stats(self):
+        for key in self.sorted_keys():
+            sizes = self.stats[key]
+            sum = 0
+            for s in sizes: sum += s
+            print "%s: %d times, avg size: %f" % (self.key_string(key), 
len(sizes), sum/len(sizes))
+            if self.do_stats_sizes:
+                print "       All sizes: %s" % sizes
+
+_stats = StorageStatistics()
+
+def activate_statistics(log=False, statistics=False, statstics_sizes=False):
+    _stats.do_log = _stats.do_log or log
+    _stats.do_stats = _stats.do_stats or statistics
+    _stats.do_stats_sizes = _stats.do_stats_sizes or statstics_sizes
+
+def print_statistics():
+    if _stats.do_stats:
+        _stats.print_stats()
+
+def log(w_obj, operation, old_storage=None, log_classname=True):
+    _stats.log(w_obj, operation, old_storage, log_classname)
diff --git a/spyvm/test/test_miniimage.py b/spyvm/test/test_miniimage.py
--- a/spyvm/test/test_miniimage.py
+++ b/spyvm/test/test_miniimage.py
@@ -369,7 +369,7 @@
 def test_primitive_perform_with_args():
     from spyvm.test.test_primitives import _prim
     w_o = space.wrap_list([1, 2, 3])
-    w_methoddict = 
w_o.class_shadow(space)._s_superclass._s_superclass.w_methoddict()
+    w_methoddict = 
w_o.class_shadow(space).s_superclass().s_superclass().w_methoddict()
     w_methoddict.as_methoddict_get_shadow(space).sync_method_cache()
     selectors_w = w_methoddict.shadow.methoddict.keys()
     w_sel = None
diff --git a/spyvm/test/test_strategies.py b/spyvm/test/test_strategies.py
--- a/spyvm/test/test_strategies.py
+++ b/spyvm/test/test_strategies.py
@@ -1,5 +1,5 @@
 import py
-from spyvm import wrapper, model, interpreter, shadow
+from spyvm import wrapper, model, interpreter, shadow, storage_statistics
 from spyvm.error import WrapperException, FatalError
 from .util import read_image, copy_to_module, cleanup_module
 
@@ -176,20 +176,27 @@
     assert isinstance(a.shadow, shadow.ListStorageShadow)
     check_arr(a, [1.2, 2, w_nil, w_nil, w_nil])
 
-def test_statistics():
-    stats = model.StrategyStatistics()
-    stats.stat_operation("B", "old", "new", 3)
-    stats.stat_operation("B", "old", "new", 4)
-    stats.stat_operation("B", "old2", "new2", 20)
-    stats.stat_operation("B", "old", "new", 5)
-    stats.stat_operation("A", "old", "new", 1)
-    stats.stat_operation("A", "old", "new", 2)
-    stats.stat_operation("C", "old", "new", 10)
-    stats.stat_operation("C", "old", "new", 11)
+def test_statistics_stats():
+    stats = storage_statistics.StorageStatistics()
+    stats.stat_operation(stats.make_key("B", "old", "new"), 3)
+    stats.stat_operation(stats.make_key("B", "old", "new"), 4)
+    stats.stat_operation(stats.make_key("B", "old2", "new2"), 20)
+    stats.stat_operation(stats.make_key("B", "old", "new"), 5)
+    stats.stat_operation(stats.make_key("A", "old", "new"), 1)
+    stats.stat_operation(stats.make_key("A", "old", "new"), 2)
+    stats.stat_operation(stats.make_key("C", "old", "new"), 10)
+    stats.stat_operation(stats.make_key("C", "old", "new"), 11)
     keys = stats.sorted_keys()
     assert keys == [ ("A", "old", "new"), ("B", "old", "new"), ("B", "old2", 
"new2"), ("C", "old", "new") ]
     assert stats.stats[keys[0]] == [1, 2]
     assert stats.stats[keys[1]] == [3, 4, 5]
     assert stats.stats[keys[2]] == [20]
     assert stats.stats[keys[3]] == [10, 11]
+    
+def test_statistics_log():
+    stats = storage_statistics.StorageStatistics()
+    s = stats.log_operation_string(stats.make_key("Operation", "old_storage", 
"new_storage"), 22, "classname")
+    assert s == "Operation (old_storage -> new_storage) of classname size 22"
+    s = stats.log_operation_string(stats.make_key("InitialOperation", None, 
"some_new_storage"), 40, "a_classname")
+    assert s == "InitialOperation (some_new_storage) of a_classname size 40"
     
\ No newline at end of file
diff --git a/targetimageloadingsmalltalk.py b/targetimageloadingsmalltalk.py
--- a/targetimageloadingsmalltalk.py
+++ b/targetimageloadingsmalltalk.py
@@ -6,7 +6,7 @@
 from rpython.rlib import jit, rpath
 
 from spyvm import model, interpreter, squeakimage, objspace, wrapper,\
-    error, shadow
+    error, shadow, storage_statistics
 from spyvm.tool.analyseimage import create_image
 from spyvm.interpreter_proxy import VirtualMachine
 
@@ -185,12 +185,11 @@
             as_benchmark = True
             idx += 1
         elif arg == "--strategy-log":
-            model.strategy_stats.do_log = True
+            storage_statistics.activate_statistics(log=True)
         elif arg == "--strategy-stats":
-            model.strategy_stats.do_stats = True
+            storage_statistics.activate_statistics(statistics=True)
         elif arg == "--strategy-stats-with-sizes":
-            model.strategy_stats.do_stats = True
-            model.strategy_stats.do_stats_sizes = True
+            storage_statistics.activate_statistics(statistics=True, 
statstics_sizes=True)
         elif path is None:
             path = argv[idx]
         else:
@@ -224,8 +223,7 @@
     else:
         _run_image(interp)
         result = 0
-    if model.strategy_stats.do_stats:
-        model.strategy_stats.print_stats()
+    storage_statistics.print_statistics()
     return result
 
 
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to