Profiling
=========

The Python standard library includes functionality to profile code. In that mode
function invocations and the time spent in them are recorded. The `accelerate.profiler`
module extends that functionality by also recording the functions' signatures, which
is useful as often the precise control flow (and thus function performance) depends
on the argument types. For numpy array types, this includes not only the dtype
attribute, but also the array's shape.
To demonstrate this, let us define a simple dot function and profile it without
signatures, to match the behaviour of the Python standard library profile module.

.. code-block:: python

  from accelerate import profiler
  import numpy as np

  def dot(a, b):
    sum=0
    for i in range(len(a)):
        sum += a[i]*b[i]
    return sum

  a = np.arange(16, dtype=np.float32)
  b = np.arange(16, dtype=np.float32)

  p = profiler.Profile(signatures=False)
  p.enable()
  dot(a, b)
  p.disable()
  p.print_stats()

which will generate output like this::
  
         3 function calls in 0.000 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 builtins.len
        1    0.000    0.000    0.000    0.000 dot.py:7(dot)
        1    0.000    0.000    0.000    0.000 {method 'disable' of 'prof.Profiler' objects}

However, by default the `Profile` constructor's `signature` flag is set to `True`,
resulting in this output instead::
  
         3 function calls (2 primitive calls) in 0.000 seconds
  
   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 dot.py:1(disable())
      2/1    0.000    0.000    0.000    0.000 dot.py:7(dot(a:ndarray(dtype=float32, shape=(16,)), b:ndarray(dtype=float32, shape=(16,))))


For more realistic code the call graph (and thus table of function calls) is obviously
much bigger, so working with the data in tabular form is not very convenient.
The `accelerate.profiler` module therefore also provides functionality to visualize the
data. Instead of calling the `print_stats()` method we may call the `accelerate.profiler.plot()` function. Note that at this time this function may only be called from inside a notebook. Thus, assuming the above code was executed in a notebook, the following::

  In [3]: profiler.plot(p)

will results in output like this:
  
.. image:: profiling.png


The accelerate.profiler API
---------------------------
	   
.. autoclass:: accelerate.profiler.Profile
   :members: print_stats
   :inherited-members: __init__

.. autofunction:: accelerate.profiler.plot
