/Misc/diff_hotness.py

http://unladen-swallow.googlecode.com/ · Python · 156 lines · 73 code · 22 blank · 61 comment · 21 complexity · d5b1e34f14ada02cd254ff6c45684d66 MD5 · raw file

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