code: def profile(
      fn=None,
      condition=None,
      profile_class=cProfile.Profile,
      print_freq=0,
      sort_keys=None,
      print_restrictions=None,
  ):
      """Decorating a function that is profiled with a Python profiler
      such as :class:`cProfile.Profile`.
  
      **Note**: ``function`` doesn't refer to :obj:`~nnabla.function.Function`.
      A Python function.
  
      Args:
          fn (function):
              A function that is profiled. If None is specified (default), it
              returns a new decorator function. It is used when you want to
              specify optional arguments of this decorator function.
          condition (function):
              A function object which takes the same inputs with the decorated
              function, and returns a boolean value. The decorated function is
              profiled only when the ``condition`` function returns ``True``.
              By default, it returns always `True`, hence profiling is performed
              everytime the decorated function is called.
          profile_class (class):
              A profiler class such as :obj:`cProfile.Profile` and
              :obj:`Profile.Profile`. The default value is
              :obj:`cProfile.Profile`.
          print_freq (int):
              The profiling result is printed at function calls with an interval
              specified by ``print_freq``. If 0 is specified (default), the
              profiling result is only printed at the end of the Python process
              unless ``decorated_func.profiler.print_stats()`` is called
              manually.
          sort_keys (iterable):
              A list or tuple of string, which is passed to
              :meth:`pstats.Stats.sort_stats` as arguments. The default is
              ``('cumulative', 'time', 'calls')``.
          print_restriction (iterable):
              A list or tuple which is passed to
              :meth:`pstats.Stats.print_stats` as arguments. The default
              value is ``(40,)``, which results in only 40 functions inside the
              decorated function are printed in the profiling result.
  
      Returns: function
  
          A decorated function. If ``fn`` is ``None``, a new decorator function
          with optional arguments specified.
  
      Example:
  
          By decorating a function as following, the profling result is printed
          at the end of the Python process.
  
          .. code-block:: python
  
              from nnabla.utils import function_profile
  
              @function_profile.profile
              def foo(a, b, c=None, d=None):
                  ...
  
          If you want to manually print the profiling result so far, use
          :meth:`FunctionProfile.print_stats`
          of the :obj:`FunctionProfile` object
          attached to the decorated function as ``profiler`` attribute.
  
          .. code-block:: python
  
              foo.profiler.print_stats()
  
          If you want to profile the function only when a specific argument is
          passed to, use the ``condition`` argument as following.
  
          .. code-block:: python
  
              def profile_only_if_c_is_not_none(a, b, c=None, d=None):
                  return c is not None
  
              @function_profile.profile(condition=profile_only_if_c_is_not_none)
              def foo(a, b, c=None, d=None):
                  ...
  
  
      """
      if fn is None:
  
          def new_decorator(fn):
              return profile(
                  fn,
                  condition=condition,
                  profile_class=profile_class,
                  print_freq=print_freq,
                  sort_keys=sort_keys,
                  print_restrictions=print_restrictions,
              )
  
          return new_decorator
      profiler = FunctionProfile(
          fn,
          condition=condition,
          profile_class=profile_class,
          print_freq=print_freq,
          sort_keys=sort_keys,
          print_restrictions=print_restrictions,
      )
  
      def new_fn(*args, **kw):
          return profiler(*args, **kw)
  
      new_fn.__name__ = fn.__name__
      new_fn.__doc__ = fn.__doc__
      new_fn.__dict__ = fn.__dict__
      new_fn.__module__ = fn.__module__
      new_fn.profiler = profiler
      return new_fn
  
file_path: nnabla/python/src/nnabla/utils/function_profile.py
imports: import atexit
  import cProfile
  import pstats
label_desc: Use image node_id 2 for calling a global function with example usage: profile(fn, condition, profile_class, print_freq, sort_keys, print_restrictions) and returns: new_fn, new_decorator, profiler, profile