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

[Relay] Add compiler pass tutorial docs #2746

Merged
merged 6 commits into from
Apr 15, 2019
Merged

Conversation

weberlo
Copy link
Contributor

@weberlo weberlo commented Mar 7, 2019

Here's a rough draft. There are some concerns I have already.

Is this too specific to the constant folding pass? I'd like to make it sufficiently general, but at the same time, depth on a single pass seems valuable.

Who should be the target audience? Do they already know how to use visitors and mutators? Or do they not know what an AST is?

Is constant folding a good example? @MarisaKirisame suggested the ANF-to-GNF pass might be a better fit.

Lemme know what you think and if I should ping anyone else.

@jroesch @slyubomirsky @MarisaKirisame


TODO: The content of this section should be almost identical to the
corresponding section in ``docs/dev/relay_add_op.rst``. Can we just link to
there?
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also curious about this point.

Copy link
Contributor

Choose a reason for hiding this comment

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

Linking is good


Compiler passes can be used to collect data about Relay programs or transform
them in various ways. The base class used to traverse programs is
``ExprFunctor``. Depending on what you want to do, there are subclasses
Copy link
Contributor

Choose a reason for hiding this comment

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

I recommend using :code: for class names and other such constructs, as using the backticks without :code: results in italics rather than typewriter font.


Adding a Compiler Pass to Relay
===============================

Copy link
Contributor

@slyubomirsky slyubomirsky Mar 7, 2019

Choose a reason for hiding this comment

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

High-level remark about this introductory section: It should be emphasized that writing passes is the key to adding features to Relay because it is how you traverse the AST and inspect the program.

Copy link
Contributor

Choose a reason for hiding this comment

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

Another good point to emphasize is that Relay's "built in" core language features are implemented using the same pass interface, as a point to show how powerful those are: autodiff and type inference are implemented as passes.


Writing an Expression Visitor
-----------------------------

Copy link
Contributor

@slyubomirsky slyubomirsky Mar 7, 2019

Choose a reason for hiding this comment

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

It might be good to mention that the recommended practice for writing Visitors is to have a separate public interface method (like Check() in the example) rather than using VisitExpr(). This is because there sometimes needs to be some bookkeeping done before invoking the top-level recursion.

Also you should explain that VisitExpr() (no underscore) is the call necessary for recursive traversal, since the example does not include a recursive traversal (is there an easy enough case that does have one?)

In Relay, there are three types of nodes that we want to perform constant
folding on:

- ``LetNode``s
Copy link
Contributor

@slyubomirsky slyubomirsky Mar 7, 2019

Choose a reason for hiding this comment

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

FYI I don't think RST permits having a character follow the backtick, so best to find a different way to phrase this (I don't know how to escape the "s").

- ``LetNode``s
- ``TupleItemGetNode``s
- ``CallNode``s

Copy link
Contributor

Choose a reason for hiding this comment

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

Code snippets would be more informative and illustrative for these paragraphs.

implementations for all node types. Each ``VisitExpr_`` method visits all of
its node fields and creates a new node if any of the resulting fields have
changed. Otherwise, the original node is returned.

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 the description of a mutator vs visitor should come earlier and be more detailed, since the visitor and mutator are very important base classes that anyone writing a pass will have to interact with.

corresponding section in ``docs/dev/relay_add_op.rst``. Can we just link to
there?

Summary
Copy link
Contributor

Choose a reason for hiding this comment

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

This summary seems unnecessary since it does not recap the points from the different sections (nor do I believe that should be at the end; a high-level summary would be best for the beginning).

}

private:
std::unordered_map<Expr, bool, NodeHash, NodeEqual> memo_;

void VisitExpr_(const ConstantNode* n) final {
memo_[GetRef<Constant>(n)] = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a test case where the previous default behavior fails? This would be a good regression to add a test for.

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 added this to the patch, mainly to discuss it.

I believe the code before is semantically equivalent to what I've changed it to, so I'm curious why the ConstantNode case was baked into Check. Is it just for performance purposes? If not, then I think this change just increases readability.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I see. It looks like it might be to avoid having constant nodes in the map (trying to reduce memory usage because constant nodes are a trivial case?). Not sure whether the change is necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I'm pretty sure it's an optimization now, so I don't think we should change the actual code. But in the tutorial, I'm thinking about having the code snippet differ from the actual code, just for for pedagogical reasons.

@slyubomirsky
Copy link
Contributor

Re: your general questions in the top-level post, I think such a tutorial should try to be general in scope since Relay compiler passes are really powerful and we want users to make use of that power. I am also not sure precisely who comprises the target audience, but it would be safe to assume that even if they are familiar with the visitor pattern (as most programmers might be expected to be or would easily be able to read up on it), it would be helpful to give specific examples and explain how Relay's visitors are set up.

@MarisaKirisame
Copy link
Contributor

LGTM

@weberlo
Copy link
Contributor Author

weberlo commented Mar 10, 2019

Thanks for the feedback, @slyubomirsky. I've tried to incorporate it in the most recent commit, and I've added some more questions under TODOs.

Adding more reviewers! @srkreddy1238 @yongwww

Be as nitpicky as you can.

@weberlo
Copy link
Contributor Author

weberlo commented Mar 20, 2019

@jroesch @slyubomirsky @MarisaKirisame @srkreddy1238 @yongwww

Pinging reviewers and adding @icemelon9

@yongwww
Copy link
Member

yongwww commented Mar 21, 2019

adding @zhiics

helper method ``ValueToExpr`` to allow us to place the evaluated expression
back into the AST.

TODO: Why does the ``CallNode`` case call ``VisitExpr_`` and not
Copy link
Contributor

Choose a reason for hiding this comment

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

ExprMutator::VisitExpr_(call) use the default implementation for call, which is just recurse
ExprMutator::VisitExpr(call) do dispatch and reach here again, which is infinite loop

At a high level, there are three key components to writing a pass:

- Creating one or more C++ classes that traverse the program
- Registering an API endpoint with the ``TVM_REGISTER_API`` macro that performs the pass
Copy link
Member

Choose a reason for hiding this comment

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

We probably want to mention that it is a TVM packed function? Packed function has been mentioned in different docs.

The base class used to traverse Relay programs is ``ExprFunctor``. The public
interface it provides is a ``VisitExpr`` method that takes an expression and
zero or more arguments and returns an instance of some type. When you extend
this class, you define the AST traversal pattern by providing implementations
Copy link
Member

Choose a reason for hiding this comment

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

s/providing/overriding/

~~~~~~~~~~~~~~~~~~~

``ExprVisitor`` is for passes that don't modify the program and instead
collect information about the program. With this class, ``VisitExpr`` and the
Copy link
Member

Choose a reason for hiding this comment

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

instead perform certain program analysis and collect information

}

Note that we're calling ``VisitExpr`` and not ``VisitExpr_`` here. This way,
routing to the appropriate implementation is taken care of for you, and you
Copy link
Member

Choose a reason for hiding this comment

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

I think we can probably mention here that calling VisitExpr will again dispatch to the correct or needed VisitExpr_ depending on the node type of each component of a IfNode. I meant we probably need to briefly talk about the functionality of VisitExpr` as well.

result_ = true;
}

where ``result_`` is a field. In this case, we don't need to further recurse
Copy link
Member

Choose a reason for hiding this comment

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

I think this paragraph is not quite clear why we need result_ and I don't see how we stopped recursing on this visit.

TODO: Why does the ``CallNode`` case call ``VisitExpr_`` and not
``VisitExpr``?

Registering an API Endpoint
Copy link
Member

Choose a reason for hiding this comment

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

Probably provide a pointer to the packed function doc?

@MarisaKirisame
Copy link
Contributor

Add "add to pass.h" or "register in pass manager".

@icemelon icemelon added the status: need update need update based on feedbacks label Mar 29, 2019
@icemelon
Copy link
Member

@weberlo Could you update the pr based on the comments?


And the pass can now be used in C++ and Python, though it's a good idea to
wrap the API in Python, as described in :ref:`relay-add-op`. More detail
about registration can be found in :doc:`runtime.md`.
Copy link
Contributor Author

@weberlo weberlo Mar 30, 2019

Choose a reason for hiding this comment

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

One problem when building these docs is that the :doc:runtime.md reference doesn't get resolved. And we can't use the normal :ref:ayy-lmao mechanism to point to those docs, because they're in Markdown, and not RST. If nobody knows how RST<->Markdown interop works, I'll need to figure that out.

Copy link
Member

Choose a reason for hiding this comment

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

Can we just rewrite it into rst?

Copy link
Contributor

Choose a reason for hiding this comment

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

I've found it successful to add explicit references into docs and use :ref: to point to those references (see the Relay lang ref for examples)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can we just rewrite it into rst?

Yeah. That seems like the way to go. Gonna rewrite it in rust rst today.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So uhh... why are the docs written in rst? It doesn't seem to highlight C or C++ very well and figuring out how to use its syntax feels like a puzzle.

@weberlo
Copy link
Contributor Author

weberlo commented Apr 10, 2019

@jroesch @slyubomirsky @icemelon9 @zhiics Can y'all give another round of review? Let's try to get this merged in soon.

@jroesch jroesch merged commit fbb7489 into apache:master Apr 15, 2019
@jroesch
Copy link
Member

jroesch commented Apr 15, 2019

I merged the initial version, improvements are welcome!

wweic pushed a commit to wweic/tvm that referenced this pull request May 13, 2019
* Add Relay compiler pass tutorial docs

* Add Python API hook wrapping step

* Incorporate feedback

* More doc iteration

* Mooooore iteration

* Rewrite `runtime.md` in rst
wweic pushed a commit to neo-ai/tvm that referenced this pull request May 13, 2019
* Add Relay compiler pass tutorial docs

* Add Python API hook wrapping step

* Incorporate feedback

* More doc iteration

* Mooooore iteration

* Rewrite `runtime.md` in rst
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: need update need update based on feedbacks
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants