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 17Analyze model, including shape, params, time, memory, structure, etc. 18""" 19from __future__ import absolute_import 20from __future__ import division 21from __future__ import print_function 22 23import sys 24 25import six 26 27from google.protobuf import message 28from tensorflow.core.profiler import tfprof_options_pb2 29from tensorflow.core.profiler import tfprof_output_pb2 30from tensorflow.python.eager import context 31from tensorflow.python.framework import errors 32from tensorflow.python.framework import ops 33from tensorflow.python.profiler import option_builder 34from tensorflow.python.profiler import tfprof_logger 35from tensorflow.python.util import _pywrap_tfprof as print_mdl 36from 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. 43ALL_ADVICE = { 44 'ExpensiveOperationChecker': {}, 45 'AcceleratorUtilizationChecker': {}, 46 'JobChecker': {}, # Only available internally. 47 'OperationChecker': {}, 48} 49 50 51def _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 59def _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 106def _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(v1=['profiler.Profiler']) 126class 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.compat.v1.RunMetadata() 139 _ = sess.run(..., 140 options=tf.compat.v1.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 not context.executing_eagerly(): 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 An Advise proto that contains 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(v1=['profiler.profile']) 310def 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/blob/master/tensorflow/core/profiler/g3doc/python_api.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 not context.executing_eagerly(): 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(v1=['profiler.advise']) 385def 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 not context.executing_eagerly(): 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