-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Changes from 15 commits
7c9d5dd
c0bc83b
af35621
43d40c4
42df16e
0485c45
7d81b58
4f8b8e4
3c666b5
18317ce
66596a9
98d4fbf
229991b
80aaa37
b6083aa
2c201c1
b0a270f
b5cffef
fd32622
078db70
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -123,16 +124,22 @@ Python APIs to create a compilation pipeline using pass context. | |
|
||
class PassContextNode : public Object { | ||
public: | ||
ErrorReporter err_reporter; | ||
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: | ||
|
@@ -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 | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I fixed wordings.
The There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. 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``. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. instrument points There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: execution There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add exception situations. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. But it's a little long section. Please help review. Thanks:) There was a problem hiding this comment. Choose a reason for hiding this commentThe 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* There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
.. 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`_) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Timing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. Thanks! |
||
|
||
PrintBefore(TODO) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could you give a short explanation what these are? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
|
||
PrintAfter(TODO) | ||
|
||
Python Frontend | ||
~~~~~~~~~~~~~~~ | ||
|
||
|
@@ -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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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` | ||
""" | ||
|
||
|
||
|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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): | ||
|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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() |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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: