Home | History | Annotate | Download | only in profiler
      1 # Copyright 2016 The TensorFlow Authors. All Rights Reserved.
      2 #
      3 # Licensed under the Apache License, Version 2.0 (the "License");
      4 # you may not use this file except in compliance with the License.
      5 # You may obtain a copy of the License at
      6 #
      7 #     http://www.apache.org/licenses/LICENSE-2.0
      8 #
      9 # Unless required by applicable law or agreed to in writing, software
     10 # distributed under the License is distributed on an "AS IS" BASIS,
     11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 # See the License for the specific language governing permissions and
     13 # limitations under the License.
     14 # ==============================================================================
     15 """Model Analyzer.
     16 
     17 Analyze model, including shape, params, time, memory, structure, etc.
     18 """
     19 from __future__ import absolute_import
     20 from __future__ import division
     21 from __future__ import print_function
     22 
     23 import sys
     24 
     25 import six
     26 
     27 from google.protobuf import message
     28 from tensorflow.core.profiler import tfprof_options_pb2
     29 from tensorflow.core.profiler import tfprof_output_pb2
     30 from tensorflow.python import pywrap_tensorflow as print_mdl
     31 from tensorflow.python.eager import context
     32 from tensorflow.python.framework import errors
     33 from tensorflow.python.framework import ops
     34 from tensorflow.python.profiler import option_builder
     35 from tensorflow.python.profiler import tfprof_logger
     36 from tensorflow.python.util.tf_export import tf_export
     37 
     38 _DEFAULT_PROFILE_OPTIONS = 0
     39 _DEFAULT_ADVISE_OPTIONS = 0
     40 
     41 # The following options are for 'advise' cmd.
     42 # Show all advice.
     43 ALL_ADVICE = {
     44     'ExpensiveOperationChecker': {},
     45     'AcceleratorUtilizationChecker': {},
     46     'JobChecker': {},  # Only available internally.
     47     'OperationChecker': {},
     48 }
     49 
     50 
     51 def _graph_string(graph):
     52   """Helper to serialize a graph to string."""
     53   if graph:
     54     return graph.as_graph_def(add_shapes=True).SerializeToString()
     55   else:
     56     return b''
     57 
     58 
     59 def _build_options(options):
     60   """Build tfprof.OptionsProto.
     61 
     62   Args:
     63     options: A dictionary of options.
     64   Returns:
     65     tfprof.OptionsProto.
     66   """
     67   opts = tfprof_options_pb2.OptionsProto()
     68   opts.max_depth = options.get('max_depth', 10)
     69   opts.min_bytes = options.get('min_bytes', 0)
     70   opts.min_peak_bytes = options.get('min_peak_bytes', 0)
     71   opts.min_residual_bytes = options.get('min_residual_bytes', 0)
     72   opts.min_output_bytes = options.get('min_output_bytes', 0)
     73   opts.min_micros = options.get('min_micros', 0)
     74   opts.min_accelerator_micros = options.get('min_accelerator_micros', 0)
     75   opts.min_cpu_micros = options.get('min_cpu_micros', 0)
     76   opts.min_params = options.get('min_params', 0)
     77   opts.min_float_ops = options.get('min_float_ops', 0)
     78   opts.min_occurrence = options.get('min_occurrence', 0)
     79 
     80   opts.step = options.get('step', -1)
     81 
     82   opts.order_by = options.get('order_by', 'name')
     83 
     84   for p in options.get('account_type_regexes', []):
     85     opts.account_type_regexes.append(p)
     86   for p in options.get('start_name_regexes', []):
     87     opts.start_name_regexes.append(p)
     88   for p in options.get('trim_name_regexes', []):
     89     opts.trim_name_regexes.append(p)
     90   for p in options.get('show_name_regexes', []):
     91     opts.show_name_regexes.append(p)
     92   for p in options.get('hide_name_regexes', []):
     93     opts.hide_name_regexes.append(p)
     94   opts.account_displayed_op_only = options.get('account_displayed_op_only',
     95                                                False)
     96 
     97   for p in options.get('select', []):
     98     opts.select.append(p)
     99 
    100   opts.output = options.get('output', 'stdout')
    101   opts.dump_to_file = options.get('dump_to_file', '')
    102 
    103   return opts
    104 
    105 
    106 def _build_advisor_options(options):
    107   """Build tfprof.AdvisorOptionsProto.
    108 
    109   Args:
    110     options: A dictionary of options. See ALL_ADVICE example.
    111   Returns:
    112     tfprof.AdvisorOptionsProto.
    113   """
    114   opts = tfprof_options_pb2.AdvisorOptionsProto()
    115   if options is None:
    116     return opts
    117   for checker, checker_opts in six.iteritems(options):
    118     checker_ops_pb = tfprof_options_pb2.AdvisorOptionsProto.CheckerOption()
    119     for k, v in six.iteritems(checker_opts):
    120       checker_ops_pb[k] = v
    121     opts.checkers[checker].MergeFrom(checker_ops_pb)
    122   return opts
    123 
    124 
    125 @tf_export('profiler.Profiler')
    126 class Profiler(object):
    127   """TensorFlow multi-step profiler.
    128 
    129   https://github.com/tensorflow/tensorflow/tree/master/tensorflow/core/profiler/README.md
    130 
    131   ```python
    132   Typical use case:
    133     # Currently we are only allowed to create 1 profiler per process.
    134     profiler = Profiler(sess.graph)
    135 
    136     for i in xrange(total_steps):
    137       if i % 10000 == 0:
    138         run_meta = tf.RunMetadata()
    139         _ = sess.run(...,
    140                      options=tf.RunOptions(
    141                          trace_level=tf.RunOptions.FULL_TRACE),
    142                      run_metadata=run_meta)
    143         profiler.add_step(i, run_meta)
    144 
    145         # Profile the parameters of your model.
    146         profiler.profile_name_scope(options=(option_builder.ProfileOptionBuilder
    147             .trainable_variables_parameter()))
    148 
    149         # Or profile the timing of your model operations.
    150         opts = option_builder.ProfileOptionBuilder.time_and_memory()
    151         profiler.profile_operations(options=opts)
    152 
    153         # Or you can generate a timeline:
    154         opts = (option_builder.ProfileOptionBuilder(
    155                 option_builder.ProfileOptionBuilder.time_and_memory())
    156                 .with_step(i)
    157                 .with_timeline_output(filename).build())
    158         profiler.profile_graph(options=opts)
    159       else:
    160         _ = sess.run(...)
    161     # Auto detect problems and generate advice.
    162     profiler.advise()
    163   ```
    164   """
    165 
    166   def __init__(self, graph=None, op_log=None):
    167     """Constructor.
    168 
    169     Args:
    170       graph: tf.Graph. If None and eager execution is not enabled, use
    171           default graph.
    172       op_log: optional. tensorflow::tfprof::OpLogProto proto. Used to define
    173           extra op types.
    174     """
    175     if not graph and context.in_graph_mode():
    176       graph = ops.get_default_graph()
    177     self._coverage = 0.0
    178     self._graph = graph
    179     # pylint: disable=protected-access
    180     op_log = tfprof_logger.merge_default_with_oplog(
    181         self._graph, op_log=op_log)
    182     # pylint: enable=protected-access
    183     print_mdl.NewProfiler(
    184         _graph_string(self._graph), op_log.SerializeToString())
    185 
    186   def __del__(self):
    187     print_mdl.DeleteProfiler()
    188 
    189   def add_step(self, step, run_meta):
    190     """Add statistics of a step.
    191 
    192     Args:
    193       step: int, An id used to group one or more different `run_meta` together.
    194           When profiling with the profile_xxx APIs, user can use the `step`
    195           id in the `options` to profile these `run_meta` together.
    196       run_meta: RunMetadata proto that contains statistics of a session run.
    197     """
    198     # pylint: disable=protected-access
    199     op_log = tfprof_logger.merge_default_with_oplog(
    200         self._graph, run_meta=run_meta)
    201     # pylint: enable=protected-access
    202     # TODO(xpan): P1: Better to find the current graph.
    203     self._coverage = print_mdl.AddStep(step, _graph_string(self._graph),
    204                                        run_meta.SerializeToString(),
    205                                        op_log.SerializeToString())
    206 
    207   def profile_python(self, options):
    208     """Profile the statistics of the Python codes.
    209 
    210       By default, it shows the call stack from root. To avoid
    211       redundant output, you may use options to filter as below
    212         options['show_name_regexes'] = ['.*my_code.py.*']
    213 
    214     Args:
    215       options: A dict of options. See core/profiler/g3doc/options.md.
    216     Returns:
    217       a MultiGraphNodeProto that records the results.
    218     """
    219     opts = _build_options(options)
    220     tfprof_node = tfprof_output_pb2.MultiGraphNodeProto()
    221     try:
    222       tfprof_node.ParseFromString(
    223           print_mdl.Profile('code'.encode('utf-8'), opts.SerializeToString()))
    224     except message.DecodeError as e:
    225       sys.stderr.write('Cannot parse returned proto: %s.\n' % e)
    226     return tfprof_node
    227 
    228   def profile_operations(self, options):
    229     """Profile the statistics of the Operation types (e.g. MatMul, Conv2D).
    230 
    231     Args:
    232       options: A dict of options. See core/profiler/g3doc/options.md.
    233     Returns:
    234       a MultiGraphNodeProto that records the results.
    235     """
    236     opts = _build_options(options)
    237     tfprof_node = tfprof_output_pb2.MultiGraphNodeProto()
    238     try:
    239       tfprof_node.ParseFromString(
    240           print_mdl.Profile('op'.encode('utf-8'), opts.SerializeToString()))
    241     except message.DecodeError as e:
    242       sys.stderr.write('Cannot parse returned proto: %s.\n' % e)
    243     return tfprof_node
    244 
    245   def profile_name_scope(self, options):
    246     """Profile the statistics of graph nodes, organized by name scope.
    247 
    248     Args:
    249       options: A dict of options. See core/profiler/g3doc/options.md.
    250     Returns:
    251       a GraphNodeProto that records the results.
    252     """
    253     opts = _build_options(options)
    254     tfprof_node = tfprof_output_pb2.GraphNodeProto()
    255     try:
    256       tfprof_node.ParseFromString(
    257           print_mdl.Profile('scope'.encode('utf-8'), opts.SerializeToString()))
    258     except message.DecodeError as e:
    259       sys.stderr.write('Cannot parse returned proto: %s.\n' % e)
    260     return tfprof_node
    261 
    262   def profile_graph(self, options):
    263     """Profile the statistics of graph nodes, organized by dataflow graph.
    264 
    265     Args:
    266       options: A dict of options. See core/profiler/g3doc/options.md.
    267     Returns:
    268       a GraphNodeProto that records the results.
    269     """
    270     opts = _build_options(options)
    271     tfprof_node = tfprof_output_pb2.GraphNodeProto()
    272     try:
    273       tfprof_node.ParseFromString(
    274           print_mdl.Profile('graph'.encode('utf-8'), opts.SerializeToString()))
    275     except message.DecodeError as e:
    276       sys.stderr.write('Cannot parse returned proto: %s.\n' % e)
    277     return tfprof_node
    278 
    279   def advise(self, options):
    280     """Automatically detect problems and generate reports.
    281 
    282     Args:
    283       options: A dict of options. See ALL_ADVICE example above.
    284     Returns:
    285       A Advise proto that conains the reports from all checkers.
    286     """
    287     advise_pb = tfprof_output_pb2.AdviceProto()
    288     opts = _build_advisor_options(options)
    289     advise_pb.ParseFromString(
    290         print_mdl.Profile('advise'.encode('utf-8'), opts.SerializeToString()))
    291     return advise_pb
    292 
    293   def serialize_to_string(self):
    294     """Serialize the ProfileProto to a binary string.
    295 
    296       Users can write it to file for offline analysis by tfprof commandline
    297       or graphical interface.
    298 
    299     Returns:
    300       ProfileProto binary string.
    301     """
    302     return print_mdl.SerializeToString()
    303 
    304   def _write_profile(self, filename):
    305     """Writes the profile to a file."""
    306     print_mdl.WriteProfile(filename)
    307 
    308 
    309 @tf_export('profiler.profile')
    310 def profile(graph=None,
    311             run_meta=None,
    312             op_log=None,
    313             cmd='scope',
    314             options=_DEFAULT_PROFILE_OPTIONS):
    315   """Profile model.
    316 
    317     Tutorials and examples can be found in:
    318     https://github.com/tensorflow/tensorflow/tree/master/tensorflow/core/profiler/README.md
    319 
    320   Args:
    321     graph: tf.Graph. If None and eager execution is not enabled, use
    322         default graph.
    323     run_meta: optional tensorflow.RunMetadata proto. It is necessary to
    324         to support run time information profiling, such as time and memory.
    325     op_log: tensorflow.tfprof.OpLogProto proto. User can assign "types" to
    326         graph nodes with op_log. "types" allow user to flexibly group and
    327         account profiles using options['accounted_type_regexes'].
    328     cmd: string. Either 'op', 'scope', 'graph' or 'code'.
    329         'op' view organizes profile using operation type. (e.g. MatMul)
    330         'scope' view organizes profile using graph node name scope.
    331         'graph' view organizes profile using graph node inputs/outputs.
    332         'code' view organizes profile using Python call stack.
    333     options: A dict of options. See core/profiler/g3doc/options.md.
    334   Returns:
    335     If cmd is 'scope' or 'graph', returns GraphNodeProto proto.
    336     If cmd is 'op' or 'code', returns MultiGraphNodeProto proto.
    337     Side effect: stdout/file/timeline.json depending on options['output']
    338   """
    339   if not graph and context.in_graph_mode():
    340     graph = ops.get_default_graph()
    341 
    342   if options == _DEFAULT_PROFILE_OPTIONS:
    343     options = (option_builder.ProfileOptionBuilder
    344                .trainable_variables_parameter())
    345   # pylint: disable=protected-access
    346   op_log = tfprof_logger.merge_default_with_oplog(
    347       graph, op_log, run_meta, add_trace=cmd == 'code')
    348   # pylint: enable=protected-access
    349 
    350   opts = _build_options(options)
    351 
    352   run_meta_str = run_meta.SerializeToString() if run_meta else b''
    353 
    354   graph_str = _graph_string(graph)
    355 
    356   if cmd == 'code' or cmd == 'op':
    357     tfprof_node = tfprof_output_pb2.MultiGraphNodeProto()
    358     ret = print_mdl.PrintModelAnalysis(graph_str, run_meta_str,
    359                                        op_log.SerializeToString(),
    360                                        cmd.encode('utf-8'),
    361                                        opts.SerializeToString())
    362     try:
    363       tfprof_node.ParseFromString(ret)
    364     except message.DecodeError as e:
    365       sys.stderr.write('Cannot parse returned proto: %s.\n' % e)
    366 
    367   elif cmd == 'graph' or cmd == 'scope':
    368     tfprof_node = tfprof_output_pb2.GraphNodeProto()
    369     ret = print_mdl.PrintModelAnalysis(graph_str, run_meta_str,
    370                                        op_log.SerializeToString(),
    371                                        cmd.encode('utf-8'),
    372                                        opts.SerializeToString())
    373     try:
    374       tfprof_node.ParseFromString(ret)
    375     except message.DecodeError as e:
    376       sys.stderr.write('Cannot parse returned proto: %s.\n' % e)
    377   else:
    378     raise errors.InvalidArgumentError(
    379         None, None, 'unknown cmd: %s\n' % cmd)
    380 
    381   return tfprof_node
    382 
    383 
    384 @tf_export('profiler.advise')
    385 def advise(graph=None, run_meta=None, options=_DEFAULT_ADVISE_OPTIONS):
    386   """Auto profile and advise.
    387 
    388     Builds profiles and automatically check anomalies of various
    389     aspects. For more details:
    390     https://github.com/tensorflow/tensorflow/tree/master/tensorflow/core/profiler/README.md
    391 
    392   Args:
    393     graph: tf.Graph. If None and eager execution is not enabled, use
    394         default graph.
    395     run_meta: optional tensorflow.RunMetadata proto. It is necessary to
    396         to support run time information profiling, such as time and memory.
    397     options: see ALL_ADVICE example above. Default checks everything.
    398   Returns:
    399     Returns AdviceProto proto
    400   """
    401   if not graph and context.in_eager_execution():
    402     graph = ops.get_default_graph()
    403 
    404   if options == _DEFAULT_ADVISE_OPTIONS:
    405     options = ALL_ADVICE.copy()
    406 
    407   # pylint: disable=protected-access
    408   op_log = tfprof_logger.merge_default_with_oplog(
    409       graph, None, run_meta, add_trace=True)
    410   # pylint: enable=protected-access
    411 
    412   run_meta_str = run_meta.SerializeToString() if run_meta else b''
    413 
    414   opts = _build_advisor_options(options)
    415   ret = tfprof_output_pb2.AdviceProto()
    416   ret.ParseFromString(
    417       print_mdl.PrintModelAnalysis(
    418           _graph_string(graph), run_meta_str, op_log.SerializeToString(),
    419           'advise'.encode('utf-8'), opts.SerializeToString()))
    420   return ret
    421