eval_gc_nvp.py 5.28 KB
Newer Older
1 2 3 4 5 6 7 8
#!/usr/bin/env python
#
# Copyright 2015 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""This script is used to analyze GCTracer's NVP output."""

9

10
from argparse import ArgumentParser
11
from copy import deepcopy
12
from gc_nvp_common import split_nvp
13
from math import log
14 15
from sys import stdin

16

17 18
class LinearBucket:
  def __init__(self, granularity):
19
    self.granularity = granularity
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

  def value_to_bucket(self, value):
    return int(value / self.granularity)

  def bucket_to_range(self, bucket):
    return (bucket * self.granularity, (bucket + 1) * self.granularity)


class Log2Bucket:
  def __init__(self, start):
    self.start = int(log(start, 2)) - 1

  def value_to_bucket(self, value):
    index = int(log(value, 2))
    index -= self.start
    if index < 0:
      index = 0
    return index

  def bucket_to_range(self, bucket):
    if bucket == 0:
      return (0, 2 ** (self.start + 1))
    bucket += self.start
    return (2 ** bucket, 2 ** (bucket + 1))


class Histogram:
  def __init__(self, bucket_trait, fill_empty):
48 49
    self.histogram = {}
    self.fill_empty = fill_empty
50
    self.bucket_trait = bucket_trait
51 52

  def add(self, key):
53
    index = self.bucket_trait.value_to_bucket(key)
54 55 56
    if index not in self.histogram:
      self.histogram[index] = 0
    self.histogram[index] += 1
57 58 59

  def __str__(self):
    ret = []
60 61
    keys = self.histogram.keys()
    keys.sort()
62 63 64 65 66 67 68 69 70
    last = keys[len(keys) - 1]
    for i in range(0, last + 1):
      (min_value, max_value) = self.bucket_trait.bucket_to_range(i)
      if i == keys[0]:
        keys.pop(0)
        ret.append("  [{0},{1}[: {2}".format(
          str(min_value), str(max_value), self.histogram[i]))
      else:
        if self.fill_empty:
71
          ret.append("  [{0},{1}[: {2}".format(
72
            str(min_value), str(max_value), 0))
73 74 75 76
    return "\n".join(ret)


class Category:
77
  def __init__(self, key, histogram):
78 79 80 81 82 83 84
    self.key = key
    self.values = []
    self.histogram = histogram

  def process_entry(self, entry):
    if self.key in entry:
      self.values.append(float(entry[self.key]))
85 86
      if self.histogram:
        self.histogram.add(float(entry[self.key]))
87

88 89 90 91 92 93 94 95 96
  def min(self):
    return min(self.values)

  def max(self):
    return max(self.values)

  def avg(self):
    return sum(self.values) / len(self.values)

97 98 99 100 101 102 103 104
  def __str__(self):
    ret = [self.key]
    ret.append("  len: {0}".format(len(self.values)))
    if len(self.values) > 0:
      ret.append("  min: {0}".format(min(self.values)))
      ret.append("  max: {0}".format(max(self.values)))
      ret.append("  avg: {0}".format(sum(self.values) / len(self.values)))
      if self.histogram:
105
        ret.append(str(self.histogram))
106 107
    return "\n".join(ret)

108 109 110 111 112 113 114 115 116
  def __repr__(self):
    return "<Category: {0}>".format(self.key)


def make_key_func(cmp_metric):
  def key_func(a):
    return getattr(a, cmp_metric)()
  return key_func

117 118 119 120

def main():
  parser = ArgumentParser(description="Process GCTracer's NVP output")
  parser.add_argument('keys', metavar='KEY', type=str, nargs='+',
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
                      help='the keys of NVPs to process')
  parser.add_argument('--histogram-type', metavar='<linear|log2>',
                      type=str, nargs='?', default="linear",
                      help='histogram type to use (default: linear)')
  linear_group = parser.add_argument_group('linear histogram specific')
  linear_group.add_argument('--linear-histogram-granularity',
                            metavar='GRANULARITY', type=int, nargs='?',
                            default=5,
                            help='histogram granularity (default: 5)')
  log2_group = parser.add_argument_group('log2 histogram specific')
  log2_group.add_argument('--log2-histogram-init-bucket', metavar='START',
                          type=int, nargs='?', default=64,
                          help='initial buck size (default: 64)')
  parser.add_argument('--histogram-omit-empty-buckets',
                      dest='histogram_omit_empty',
                      action='store_true',
                      help='omit empty histogram buckets')
  parser.add_argument('--no-histogram', dest='histogram',
                      action='store_false', help='do not print histogram')
140
  parser.set_defaults(histogram=True)
141
  parser.set_defaults(histogram_omit_empty=False)
142 143 144 145
  parser.add_argument('--rank', metavar='<no|min|max|avg>',
                      type=str, nargs='?',
                      default="no",
                      help="rank keys by metric (default: no)")
146 147
  args = parser.parse_args()

148 149
  histogram = None
  if args.histogram:
150 151 152 153 154 155
    bucket_trait = None
    if args.histogram_type == "log2":
      bucket_trait = Log2Bucket(args.log2_histogram_init_bucket)
    else:
      bucket_trait = LinearBucket(args.linear_histogram_granularity)
    histogram = Histogram(bucket_trait, not args.histogram_omit_empty)
156 157

  categories = [ Category(key, deepcopy(histogram))
158 159 160 161 162 163 164 165 166 167
                 for key in args.keys ]

  while True:
    line = stdin.readline()
    if not line:
      break
    obj = split_nvp(line)
    for category in categories:
      category.process_entry(obj)

168 169 170
  if args.rank != "no":
    categories = sorted(categories, key=make_key_func(args.rank), reverse=True)

171 172 173 174 175 176
  for category in categories:
    print(category)


if __name__ == '__main__':
  main()