[pypy-commit] lang-smalltalk storage: Added an option to also aggregate and log the classes of elements that cause an object to switch to another storage strategy.

anton_gulenko noreply at buildbot.pypy.org
Mon Jul 7 13:16:53 CEST 2014


Author: Anton Gulenko <anton.gulenko at googlemail.com>
Branch: storage
Changeset: r865:13350a81184e
Date: 2014-07-03 15:37 +0200
http://bitbucket.org/pypy/lang-smalltalk/changeset/13350a81184e/

Log:	Added an option to also aggregate and log the classes of elements
	that cause an object to switch to another storage strategy.

diff --git a/spyvm/model.py b/spyvm/model.py
--- a/spyvm/model.py
+++ b/spyvm/model.py
@@ -612,16 +612,16 @@
         assert shadow, "The shadow has not been initialized yet!"
         return shadow
     
-    def switch_shadow(self, new_shadow):
+    def switch_shadow(self, new_shadow, w_element=None):
         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)
+        self.log_storage("Switched", old_shadow, w_element=w_element)
     
     def store_with_new_storage(self, new_storage, n0, w_val):
         space = self.space()
-        self.switch_shadow(new_storage(space, self, self.size()))
+        self.switch_shadow(new_storage(space, self, self.size()), w_element=w_val)
         self.store(space, n0, w_val)
     
     def space(self):
diff --git a/spyvm/storage_logger.py b/spyvm/storage_logger.py
--- a/spyvm/storage_logger.py
+++ b/spyvm/storage_logger.py
@@ -1,45 +1,65 @@
+
+class LogEntry(object):
+    def __init__(self):
+        self.slots = 0
+        self.objects = 0
+        self.element_classnames = {}
+        
+    def add(self, size, element_classname):
+        self.slots += size
+        self.objects += 1
+        if element_classname:
+            self.element_classnames[element_classname] = None
+    
+    def classnames(self):
+        if len(self.element_classnames) > 0:
+            return self.element_classnames.keys()
+        return None
 
 class Logger(object):
     def __init__(self):
         self.active = False
         self.aggregate = False
+        self.elements = False
         self.logs = {}
     
-    def log(self, operation, old_storage, new_storage, classname, size):
+    def log(self, operation, old_storage, new_storage, classname, size, element_classname):
         if self.aggregate:
             key = (operation, old_storage, new_storage, classname)
             if key not in self.logs:
-                self.logs[key] = [0, 0]
-            tuple = self.logs[key]
-            tuple[0] += size
-            tuple[1] += 1
+                self.logs[key] = LogEntry()
+            entry = self.logs[key]
+            entry.add(size, element_classname)
         else:
-            self.output(operation, old_storage, new_storage, classname, size, 1)
+            element_classnames = [ element_classname ] if element_classname else None
+            self.output(operation, old_storage, new_storage, classname, size, 1, element_classnames)
     
     def print_aggregated_log(self):
         if not self.aggregate:
             return
-        for key, tuple in self.logs.items():
+        for key, entry in self.logs.items():
             operation, old_storage, new_storage, classname = key
-            slots, objects = tuple
-            self.output(operation, old_storage, new_storage, classname, slots, objects)
+            slots, objects, element_classnames = entry.slots, entry.objects, entry.classnames()
+            self.output(operation, old_storage, new_storage, classname, slots, objects, element_classnames)
     
-    def output(self, operation, old_storage, new_storage, classname, slots, objects):
+    def output(self, operation, old_storage, new_storage, classname, slots, objects, element_classnames):
         old_storage_string = "%s -> " % old_storage if old_storage else ""
         classname_string = " of %s" % classname if classname else ""
-        format = (operation, old_storage_string, new_storage, classname_string, slots, objects)
-        print "%s (%s%s)%s size %d objects %d" % format
+        element_string = (" elements: " + " ".join(element_classnames)) if element_classnames else ""
+        format = (operation, old_storage_string, new_storage, classname_string, slots, objects, element_string)
+        print "%s (%s%s)%s size %d objects %d%s" % format
 
 _logger = Logger()
 
-def activate(aggregate=False):
+def activate(aggregate=False, elements=False):
     _logger.active = True
-    _logger.aggregate = aggregate
+    _logger.aggregate = _logger.aggregate or aggregate
+    _logger.elements = _logger.elements or elements
 
 def print_aggregated_log():
     _logger.print_aggregated_log()
 
-def log(w_obj, operation, old_storage_object=None, log_classname=True):
+def log(w_obj, operation, old_storage_object=None, log_classname=True, w_element=None):
     if not _logger.active:
         return
     
@@ -54,6 +74,10 @@
         classname = w_obj.guess_classname()
     else:
         classname = None
+    if _logger.elements and w_element and log_classname:
+        element_classname = w_element.guess_classname()
+    else:
+        element_classname = None
     
-    _logger.log(operation, old_storage, new_storage, classname, size)
+    _logger.log(operation, old_storage, new_storage, classname, size, element_classname)
     
\ No newline at end of file
diff --git a/spyvm/tool/storagelog_parser.py b/spyvm/tool/storagelog_parser.py
--- a/spyvm/tool/storagelog_parser.py
+++ b/spyvm/tool/storagelog_parser.py
@@ -27,7 +27,7 @@
                 callback(entry)
     return parsed_entries
 
-line_pattern = re.compile("^(?P<operation>\w+) \(((?P<old>\w+) -> )?(?P<new>\w+)\)( of (?P<classname>.+))? size (?P<size>[0-9]+)( objects (?P<objects>[0-9]+))?$")
+line_pattern = re.compile("^(?P<operation>\w+) \(((?P<old>\w+) -> )?(?P<new>\w+)\)( of (?P<classname>.+))? size (?P<size>[0-9]+)( objects (?P<objects>[0-9]+))?( elements: (?P<classnames>.+( .+)+))?$")
 
 def parse_line(line, flags):
     result = line_pattern.match(line)
@@ -41,16 +41,20 @@
     classname = result.group('classname')
     size = result.group('size')
     objects = result.group('objects')
-    return LogEntry(operation, old_storage, new_storage, classname, size, objects)
+    classnames = result.group('classnames')
+    if classnames is not None:
+        classnames = classnames.split(' ')
+    return LogEntry(operation, old_storage, new_storage, classname, size, objects, classnames)
 
 class LogEntry(object):
     
-    def __init__(self, operation, old_storage, new_storage, classname, size, objects):
+    def __init__(self, operation, old_storage, new_storage, classname, size, objects, classnames):
         self.operation = str(operation)
         self.new_storage = str(new_storage)
         self.classname = str(classname)
         self.size = int(size)
         self.objects = int(objects) if objects else 1
+        self.classnames = set(classnames) if classnames else set()
         
         if old_storage is None:
             if operation == "Filledin":
@@ -83,9 +87,10 @@
 
 class Operations(object):
     
-    def __init__(self, objects=0, slots=0):
+    def __init__(self, objects=0, slots=0, element_classnames=[]):
         self.objects = objects
         self.slots = slots
+        self.element_classnames = set(element_classnames)
     
     def __str__(self, total=None):
         if self.objects == 0:
@@ -102,7 +107,9 @@
             percent_objects = ""
         slots = format(self.slots, ",d")
         objects = format(self.objects, ",d")
-        return "%s%s slots in %s%s objects (avg size: %.1f)" % (slots, percent_slots, objects, percent_objects, avg_slots)
+        classnames = (" [ elements: %s ]" % ' '.join([str(x) for x in self.element_classnames])) \
+                                    if len(self.element_classnames) else ""
+        return "%s%s slots in %s%s objects (avg size: %.1f)%s" % (slots, percent_slots, objects, percent_objects, avg_slots, classnames)
     
     def __repr__(self):
         return "%s(%s)" % (self.__str__(), object.__repr__(self))
@@ -110,6 +117,7 @@
     def add_log_entry(self, entry):
         self.slots = self.slots + entry.size
         self.objects = self.objects + entry.objects
+        self.element_classnames |= entry.classnames
     
     def __sub__(self, other):
         return Operations(self.objects - other.objects, self.slots - other.slots)
diff --git a/targetimageloadingsmalltalk.py b/targetimageloadingsmalltalk.py
--- a/targetimageloadingsmalltalk.py
+++ b/targetimageloadingsmalltalk.py
@@ -132,6 +132,7 @@
           -d|--max-stack-depth [number, default %d, <= 0 disables stack protection]
           -l|--storage-log
           -L|--storage-log-aggregate
+          -E|--storage-log-elements
           [image path, default: Squeak.image]
     """ % (argv[0], constants.MAX_LOOP_DEPTH)
 
@@ -200,6 +201,8 @@
             storage_logger.activate()
         elif arg in ["-L", "--storage-log-aggregate"]:
             storage_logger.activate(aggregate=True)
+        elif arg in ["-E", "--storage-log-elements"]:
+            storage_logger.activate(elements=True)
         elif path is None:
             path = argv[idx]
         else:


More information about the pypy-commit mailing list