Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DOCS] Add docs for Pass Instrument #8220

Merged
merged 20 commits into from
Jul 7, 2021
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/api/python/ir.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ tvm.ir
:autosummary:


tvm.instrument
--------------
.. automodule:: tvm.instrument
:members:
:imported-members:
:autosummary:


tvm.transform
-------------
.. automodule:: tvm.transform
Expand Down
7 changes: 6 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,12 @@ def git_describe_version(original_version):
"tune_network_x86.py",
"tune_network_cuda.py",
],
"dev": ["low_level_custom_pass.py", "use_pass_infra.py", "bring_your_own_datatypes.py"],
"dev": [
"low_level_custom_pass.py",
"use_pass_infra.py",
"use_pass_instrument.py",
"bring_your_own_datatypes.py",
],
}


Expand Down
224 changes: 221 additions & 3 deletions docs/dev/pass_infra.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ configure the compilation options, including optimization level and
required/disabled passes, etc. For instance, we may have a configuration which
performs all passes at ``opt_level=3`` with some disabled passes using
``disabled_pass=xx`` provided by ``PassContext``. Now we could glob all passes
at ``opt_level=3`` and exclude those in the disabled pass list.
at ``opt_level=3`` and exclude those in the disabled pass list. ``PassContext``
also provides a way to instrument all passes. See section :ref:`pass_instrument_cpp_backend`.

This class is designed for users to conveniently write the Python ``with``
syntax to perform optimizations under a certain configuration. In addition, the
Expand All @@ -123,16 +124,22 @@ Python APIs to create a compilation pipeline using pass context.

class PassContextNode : public Object {
public:
ErrorReporter err_reporter;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happened to the error reporter?

Copy link
Contributor Author

@chiwwang chiwwang Jun 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to be replaced by diag_ctx in #6274.
It is not related to this PR. I fixed it just because it's handy.
Though there aren't lots of descriptions about error reporters in pass_infra.txt, I think it should present due to this sentence:

PassContext carries useful information for an optimization pass. For example, it contains the error reporting system so optimization authors can provide diagnostic...

int opt_level{2};
tvm::Array<tvm::Expr> required_pass;
tvm::Array<tvm::Expr> disabled_pass;
mutable Optional<DiagnosticContext> diag_ctx;
Map<String, ObjectRef> config;
Array<instrument::PassInstrument> instruments;
};

class PassContext : public NodeRef {
public:
TVM_DLL static PassContext Create();
TVM_DLL static PassContext Current();
TVM_DLL void InstrumentEnterPassContext();
TVM_DLL void InstrumentExitPassContext();
TVM_DLL bool InstrumentBeforePass(const IRModule& mod, const PassInfo& info) const;
TVM_DLL void InstrumentAfterPass(const IRModule& mod, const PassInfo& info) const;
/* Other fields are omitted. */

private:
Expand Down Expand Up @@ -338,7 +345,7 @@ favorably use Python APIs to create a specific pass object.
Pass Sequential(tvm::Array<Pass> passes, PassInfo pass_info);

Pass Registration
~~~~~~~~~~~~~~~~~
^^^^^^^^^^^^^^^^^

We've covered the concept of different level of passes and the context used for
compilation. It would be interesting to see how easily users can register
Expand Down Expand Up @@ -389,6 +396,136 @@ To allow other C++ modules to apply this pass, we declare a free function in

TVM_DLL Pass FoldConstant();

.. _pass_instrument_cpp_backend:

Pass Instrument
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems like PassContext may be introduced after this section, no? Perhaps it makes sense to move it downward.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed wordings.
As for the order of introduction, It's a little embarrassing. I would like to put all Pass Instrument descriptions together and after PassContext introduction.
But the pass_infra.txt is divided to C++ backend and Python Frontend parts.
I tried to follow that. So the order of introduction becomes:

  1. PassContext C++ backend
  2. PassInstrument C++ backend
  3. PassContext Python frontend
  4. PassInstrument Python frontend

The PassContext Python frontend is after PassInstrument C++ backend.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah i see. what do you think about documenting the PassInstrument first, since that's the developer-facing thing? TVM calls InstrumentEnterPassContext.

i think explaining the C++ thing first and then the Python thing is workable. i agree it's a bit tricky to work around the split. I don't think the Python docs need to be as extensive since they mostly wrap the C++ stuff

Copy link
Contributor Author

@chiwwang chiwwang Jun 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice! But after trying and reading around, I find PassInstrument might require understanding PassContext first to know about what each instrument points means.
Perhaps we can split PassInstrument doc out once it becomes bigger (such as more and more built-in instruments are added, or new functionality added to provide more instrumentation possibility?)

I also tried to shrink Python docs a little. Thanks a lot for suggestions @areusch :)

^^^^^^^^^^^^^^^

Currently we introduce four instrument point in the life-cycle of ``PassContext``.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instrument points

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


.. code:: c++

TVM_DLL void InstrumentEnterPassContext();
TVM_DLL void InstrumentExitPassContext();
TVM_DLL bool InstrumentBeforePass(const IRModule& mod, const PassInfo& info) const;
TVM_DLL void InstrumentAfterPass(const IRModule& mod, const PassInfo& info) const;

``InstrumentEnterPassContext`` is called immediately when entering the scope
of the ``PassContext`` instance.

``InstrumentExitPassContext`` is called when leaving the scope of ``PassContext``,
or exceptions occur during the execution of passes.
This method is also called when instruments is being overriden by ``override_instruments`` in :py:class:`tvm.transform.PassContext`.
See :ref:`pass_instrument_overriden`.

``InstrumentBeforePass`` is called before execution.
``InstrumentAfterPass`` is called after executioon if the pass should be run. The behavior is like:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: execution

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Thanks!


.. code:: c++

if (pass_ctx.InstrumentBeforePass(ir_module, pass_info)) {
new_ir_module = run_pass(ir_module, pass_ctx);
pass_ctx.InstrumentAfterPass(new_ir_module, pass_info);
return new_ir_module;
}

The ``PassInstrument`` interface allow you to run arbitrary code inside above four methods.
Multiple ``PassInstrument`` instances can be registed into a single
``PassContext``. ``PassInstrument`` instances are called sequentially in the order of
``instruments`` argument passed to ``PassContext``.

``PassInstrument`` provides following interfaces:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add exception situations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. But it's a little long section. Please help review. Thanks:)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it maybe better to move python frontend ahead, use it to introduce what is a pass instrument and instrument points, then goes to the discussion in PassContext::Instrument*

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's hard to introduce python frontend first because pass_infra.txt put a big section called "Python Frontend" downward.
All python things are put there. If I don't follow this, pass_instra.txt become fragmented.
(Although I agreed it is easier to introduce call-sequences and concepts with Python code...)

.. code:: c++

namespace instrument {

class PassInstrumentNode : public Object {
public:
String name;
virtual void EnterPassContext() const = 0;
virtual void ExitPassContext() const = 0;
virtual bool ShouldRun(const IRModule& mod, const transform::PassInfo& info) const = 0;
virtual void RunBeforePass(const IRModule& mod, const transform::PassInfo& info) const = 0;
virtual void RunAfterPass(const IRModule& mod, const transform::PassInfo& info) const = 0;
/* Other fields are omitted. */
};

class PassInstrument : public ObjectRef {
public:
TVM_DEFINE_OBJECT_REF_METHODS(PassInstrument, ObjectRef, PassInstrumentNode);
};

} // namespace instrument

Python frontend are provided to implement ``PassInstrument`` quickly. See :ref:`pass_instrument_py_frontend`.

Within a ``PassContext``, the call sequence of a ``PassInstrument`` instance is like:

::

with PassContext(instruments=[pi]) # pi = a PassInstrument implementation.
pi.EnterPassContext()

if pi.ShouldRun(Pass1):
pi.RunBeforePass()
Pass1()
pi.RunAfterPass()

if pi.ShouldRun(Pass2):
pi.RunBeforePass()
Pass2()
pi.RunAfterPass()

pi.ExitPassContext()

Here is a brief introduction of relations between ``PassInstrument`` interfaces
and ``PassContext`` methods. See (`src/ir/transform.cc`_) for more details.

- ``InstrumentEnterPassContext``

* ``EnterPassContext()`` is executed in the order of ``instruments`` passed to the ``PassContext``.
* When an exception raises, ``PassContext`` disable the pass instrumentation
by clearing all registered ``PassInstrument`` instances.
* Then ``PassContext`` execute ``ExitPassContext()`` method of each ``PassInstrument``
instances which successfully finished ``EnterPassContext()``
* For example, if ``PassInstrument`` A, B, and C are registered to a ``PassContext``
and A finished ``EnterPassContext()`` while B throws an exception, then C
is never executed; ``ExitPassContext()`` of A is executed.

- ``InstrumentExitPassContext``

* ``ExitPassContext()`` of each ``PassInstrument`` instances are executed in
the order of ``instruments`` passed to the ``PassContext``.
* While an exception occurs, ``instruments`` is cleared.
* ``PassInstrument`` Instances registered after the one throwing exceptions do not execute ``ExitPassContext``.

- ``InstrumentBeforePass``

* ``ShouldRun`` is executed if the pass is not listed as a required pass.
* ``RunBeforePass`` is executed in the order of ``instruments`` if the pass is not blocked by ``ShouldRun``.
* Note that ``InstrumentBeforePass`` returns a boolean indicating whether or not the pass should be run.
* When an exception occur, it is thrown immediately.
We rely on Python Context Manager to exit ``PassContext`` safely
(meaning ``ExitPassContext`` of each instruments will be run. For C++, please refer to `include/tvm/support/with.h`_.)

- ``InstrumentAfterPass``

* ``RunAfterPass`` is executed in the order of ``instruments`` passed to the ``PassContext``.
* When an exception occur, it is thrown immediately.
We rely on Python Context Manager or ``With`` class(`include/tvm/support/with.h`_) to exit ``PassContext`` safely

Built-in Instrument
^^^^^^^^^^^^^^^^^^^

There are several built-in instruments. Those marked with *TODO* are not implemented yet.

PassTimmingInstrument (see `src/ir/instrument.cc`_)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Timing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Thanks!


PrintBefore(TODO)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you give a short explanation what these are?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


PrintAfter(TODO)

Python Frontend
~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -526,16 +663,93 @@ decorators and then invoke it. For more examples about how to customize your own
optimization pipeline and debug Relay and tir passes, please refer to the
`use pass infra`_ tutorial.


.. _pass_instrument_py_frontend:

Pass Instrument
^^^^^^^^^^^^^^^

A customizable framework to instrument passes is provided. ``PassInstrument`` classes can be registered while constructing ``PassContext``.

.. code:: python

@tvm._ffi.register_object("transform.PassContext")
class PassContext(tvm.runtime.Object):
def __init__(
self,
opt_level=2,
required_pass=None,
disabled_pass=None,
instruments=None,
config=None,
):
# ...

One can implement a ``PassInstrument`` by using the ``pass_instrument`` decorator(`python/tvm/ir/instrument.py`_) on a class implementing following methods:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Maybe it should be emphasized to use decorator, instead of overriding/subclassing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think it maybe better to emphasize use decorator instead of subclassing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


- ``enter_pass_ctx``

* This method is run when entering ``PassContext``.

- ``exit_pass_ctx``

* This method is run when exiting ``PassContext``.

- ``should_run``

* This method is run before a pass is executed. It returns a boolean
indicating whether or not the pass should be run.
* If a pass is listed as required, ``should_run`` will not have effect and not be executed.

- ``run_before_pass``

* If a pass should be run, this method is run just before pass execution.

- ``run_after_pass``

* This method is run right after a pass has been executed.

`use pass instrument`_ tutorial provides examples for how to implement ``PassInstrument`` with Python APIs.

.. _pass_instrument_overriden:

Override Instruments in Current PassContext
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

``override_instruments`` method is provided to override the ``instruments`` of current ``PassContext``.
For example, if passes are run without explicitly creating a new ``PassContext``,
one can still register ``PassInstrument`` into the global ``PassContext`` by:

.. code:: python
# Get current PassContext
cur_pass_ctx = tvm.transform.PassContext.current()
# Register new PassInstrument instance
cur_pass_ctx.override_instruments([pass_inst0, pass_inst1])
# Run Passes
mod = Pass1(mod)
mod = Pass2(mod)
# Get instrument results...e.t.c.
result0 = pass_inst0.get_result()
result1 = pass_inst1.get_result()

Note that when ``override_instruments`` is called, the ``exit_pass_ctx`` method of
old ``PassInstrument`` instances are called. Then the ``enter_pass_ctx`` method of
new ``PassInstrument`` are called.

.. _Sequential: https://pytorch.org/docs/stable/nn.html?highlight=sequential#torch.nn.Sequential

.. _Block: https://mxnet.apache.org/api/python/docs/api/gluon/block.html#gluon-block

.. _include/tvm/ir/transform.h: https://github.com/apache/tvm/blob/main/include/tvm/ir/transform.h

.. _include/tvm/support/with.h: https://github.com/apache/tvm/blob/main/include/tvm/support/with.h

.. _src/relay/ir/transform.cc: https://github.com/apache/tvm/blob/main/src/relay/ir/transform.cc

.. _src/ir/transform.cc: https://github.com/apache/tvm/blob/main/src/ir/transform.cc

.. _src/ir/instrument.cc: https://github.com/apache/tvm/blob/main/src/ir/instrument.cc

.. _src/relay/transforms/fold_constant.cc: https://github.com/apache/tvm/blob/main/src/relay/transforms/fold_constant.cc

.. _python/tvm/relay/transform/transform.py: https://github.com/apache/tvm/blob/main/python/tvm/relay/transform/transform.py
Expand All @@ -544,6 +758,10 @@ optimization pipeline and debug Relay and tir passes, please refer to the

.. _python/tvm/ir/transform.py: https://github.com/apache/tvm/blob/main/python/tvm/ir/transform.py

.. _python/tvm/ir/instrument.py: https://github.com/apache/tvm/blob/main/python/tvm/ir/instrument.py

.. _src/tir/transforms/unroll_loop.cc: https://github.com/apache/tvm/blob/main/src/tir/transforms/unroll_loop.cc

.. _use pass infra: https://github.com/apache/tvm/blob/main/tutorials/dev/use_pass_infra.py

.. _use pass instrument: https://github.com/apache/tvm/blob/main/tutorials/dev/use_pass_instrument.py
27 changes: 20 additions & 7 deletions python/tvm/ir/instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,8 @@ class PassInstrument(tvm.runtime.Object):
"""A pass instrument implementation.

Users don't need to interact with this class directly.
Instead, a `PassInstrument` instance should be created through `pass_instrument`.

See Also
--------
`pass_instrument`
Instead, a `PassInstrument` instance should be created through
:py:func:`pass_instrument`
"""


Expand Down Expand Up @@ -91,13 +88,15 @@ def pass_instrument(pi_cls=None):

Parameters
----------
pi_class :
pi_class : class
Instrument class. See example below.

Examples
--------
The following code block decorates a pass instrument class.
The following code block shows how to decorate a pass instrument class.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can delete this line.


.. code-block:: python

@tvm.instrument.pass_instrument
class SkipPass:
def __init__(self, skip_pass_name):
Expand Down Expand Up @@ -155,5 +154,19 @@ def render():
-------
string : string
The rendered string result of time profiles

Examples
--------

The following code-block shows how to use this instrument.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just delete this line.


.. code-block:: python

timing_inst = PassTimingInstrument()
with tvm.transform.PassContext(instruments=[timing_inst]):
relay_mod = relay.transform.InferType()(relay_mod)
relay_mod = relay.transform.FoldScaleAxis()(relay_mod)
# before exiting the context, get profile results.
profiles = timing_inst.render()
"""
return _ffi_instrument_api.RenderTimePassProfiles()
4 changes: 2 additions & 2 deletions python/tvm/ir/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ def __exit__(self, ptype, value, trace):
def override_instruments(self, instruments):
"""Override instruments within this PassContext.

If there are existing instruments, their exit_pass_ctx callbacks are called.
Then switching to new instruments and calling new enter_pass_ctx callbacks.
If there are existing instruments, their ``exit_pass_ctx`` callbacks are called.
Then switching to new instruments and calling new ``enter_pass_ctx`` callbacks.

instruments : Sequence[PassInstrument]
The list of pass instrument implementations.
Expand Down
3 changes: 0 additions & 3 deletions tests/python/relay/test_pass_instrument.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,6 @@ def __init__(self, id):
def exit_pass_ctx(self):
events.append(self.id + " exit ctx")

def exit_pass_ctx(self):
events.append(self.id + " exit ctx")

@pass_instrument
class PIBroken(PI):
def __init__(self, id):
Expand Down
Loading