PageRenderTime 28ms CodeModel.GetById 14ms app.highlight 11ms RepoModel.GetById 1ms app.codeStats 0ms

/Misc/diff_hotness.py

http://unladen-swallow.googlecode.com/
Python | 156 lines | 73 code | 22 blank | 61 comment | 24 complexity | d5b1e34f14ada02cd254ff6c45684d66 MD5 | raw file
  1#! /usr/bin/env python2.5
  2
  3"""Compute the differences between two hotness metric tables.
  4
  5When configured with --with-instrumentation, at interpreter-shutdown Python will
  6print, among other things, a table showing how hot the JIT judged various
  7functions to be. The format of this table is:
  8module_path:lineno (func_name) -> hotness_metric
  9
 10If you copy and paste that table into a text file, this script can parse it for
 11you and tell what effect your changes to the hotness function is having.
 12
 13$ python diff_hotness.py old_table.txt new_table.txt
 14### Newly-hot functions
 15	foo.py:9812 (startTagCloseP) now has 10343000 points
 16
 17### No-longer-hot functions
 18	foo.py:1480 (endTagOther) had 108031 points
 19
 20### Hotter functions
 21	bar.py:146 (consumeEntity) -> +14438 points
 22	foo:405 (charsUntil) -> +35 points
 23
 24### Colder functions
 25	baz.py:131 (elementInScope) -> -7403 points
 26	baz.py:201 (elementInActiveFormattingElements) -> -5916 points
 27"""
 28
 29from __future__ import with_statement
 30
 31__author__ = "collinwinter@google.com (Collin Winter)"
 32
 33# Python imports
 34import optparse
 35import re
 36import sys
 37
 38
 39# function_id -> hotness_metric
 40PARSE_LINE = re.compile(r"^(.+)\s+->\s+(\d+)$")
 41
 42def parse_table(table_data):
 43    """Parse a hotness table into its constituent pieces.
 44
 45    Args:
 46        table_data: string representing the hotness table, one function per
 47            line.
 48
 49    Returns:
 50        Dict mapping function_id (str) to hotness metric (int).
 51
 52    Raises:
 53        ValueError: if a line in the file was improperly formatted.
 54    """
 55    table = {}
 56    for i, line in enumerate(table_data.splitlines()):
 57        match = PARSE_LINE.match(line)
 58        if match:
 59            function_id, hotness_metric = match.groups()
 60            table[function_id] = int(hotness_metric)
 61        else:
 62            raise ValueError("Bad input at line %d: %r" % (i, line))
 63    return table
 64
 65
 66class DiffSummary(object):
 67    """Summary of the differences between two hotness tables.
 68
 69    Attributes (all dicts mapping function ids to ints):
 70        appeared: newly-hot functions; how hot are they now?
 71        disappeared: which functions are no longer hot; how hot were they?
 72        hotter: how much hotter are these functions than they were?
 73        colder: how much colder are these functions than they were?
 74    """
 75
 76    def __init__(self):
 77        self.appeared = {}
 78        self.disappeared = {}
 79        self.hotter = {}
 80        self.colder = {}
 81
 82
 83def diff_tables(base_table, new_table):
 84    """Compute the difference between the old and new hotness tables.
 85
 86    Args:
 87        base_table: dict from parse_table().
 88        new_table: dict from parse_table().
 89
 90    Returns:
 91        DiffSummary instance.
 92    """
 93    results = DiffSummary()
 94
 95    # Search for functions that either appeared or disappeared based on the
 96    # hotness function change.
 97    base_funcs = set(base_table)
 98    new_funcs = set(new_table)
 99    for func in (new_funcs - base_funcs):
100        results.appeared[func] = new_table[func]
101    for func in (base_funcs - new_funcs):
102        results.disappeared[func] = base_table[func]
103
104    # Search for functions that became hotter or colder based on the hotness
105    # function change.
106    common_funcs = base_funcs & new_funcs
107    for func in common_funcs:
108        diff = new_table[func] - base_table[func]
109        if diff > 0:
110            results.hotter[func] = diff
111        elif diff < 0:
112            results.colder[func] = diff
113
114    return results
115
116
117def main(argv):
118    parser = optparse.OptionParser(
119        usage="%prog base_hotness new_hotness",
120        description=("Diff two hotness function tables."))
121    options, args = parser.parse_args()
122
123    if len(args) != 2:
124        parser.error("Need to specify two hotness tables")
125    base_filename, new_filename = args
126
127    with open(base_filename) as base_file:
128        base_data = parse_table(base_file.read())
129    with open(new_filename) as new_file:
130        new_data = parse_table(new_file.read())
131
132    differences = diff_tables(base_data, new_data)
133    if differences.appeared:
134        print "### Newly-hot functions"
135        for func, hotness in differences.appeared.items():
136            print "\t%s now has %d points" % (func, hotness)
137        print
138    if differences.disappeared:
139        print "### No-longer-hot functions"
140        for func, hotness in differences.disappeared.items():
141            print "\t%s had %d points" % (func, hotness)
142        print
143    if differences.hotter:
144        print "### Hotter functions"
145        for func, diff in differences.hotter.items():
146            print "\t%s -> +%d points" % (func, diff)
147        print
148    if differences.colder:
149        print "### Colder functions"
150        for func, diff in differences.colder.items():
151            print "\t%s -> %d points" % (func, diff)
152        print
153
154
155if __name__ == "__main__":
156    main(sys.argv)