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

Updates for @log_sparse with context manager and parameters for %%viztracer #360

Merged
merged 19 commits into from
Nov 8, 2023

Conversation

AlexeyKozhevin
Copy link
Contributor

@AlexeyKozhevin AlexeyKozhevin commented Aug 4, 2023

@AlexeyKozhevin AlexeyKozhevin changed the title Fix log sparse Updates for @log_sparse with contect manager and parameters for %%viztracer Aug 10, 2023
Copy link
Owner

@gaogaotiantian gaogaotiantian left a comment

Choose a reason for hiding this comment

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

Overall the docs and the code is good. I have left some comments for the part that I personally have some concerns. Also as I pursue 100% coverage for viztracer, there may or may not be some extra tests requirement - let's see the coverage result.

@@ -32,6 +36,40 @@ def view(): # pragma: no cover

display(button)

def make_kwargs(self, line, tracer_signature, viewer_signature):
Copy link
Owner

Choose a reason for hiding this comment

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

You basically wrote an argument parser here, which would be difficult to maintain and requires a lot of tests (viztracer keeps a 100% coverage). I don't think this should be the way to go, especially considering there could be conflict arguments (for example, eliminating --once is a very bad idea for the viewer).

I don't want the magic cells to have full flexibility as the CLI, that could be unnecessarily difficult to maintain. However, having some options like --port might be beneficial. Still, I prefer a white list way instead of this auto-importing kind of way (this implementation will introduce a silent dependency that VizTracer and ServerThead can't share args with the same name, which is not ideal).

To be honest, cell magic is not the top-priority for viztracer - it would be nice if it works, but it's totally fine that it won't work under some circumstances. To leverage its full capability, the users can always use the inline VizTracer context and open the report with code.

So, I think I'd prefer this piece to be removed, and if you want some useful argument for the cell magic, use something like IPython.core.magic_arguments, which we do not need to test for its validity.

return ret

return wrapper


def _log_sparse_wrapper_with_args(stack_depth: int = 0, dynamic_tracer_check: bool = False) -> Callable:
Copy link
Owner

Choose a reason for hiding this comment

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

This function is only used once and the content is a single line. I don't see a very strong reason to abstract it out. I think we can just return the partial when the func is None in log_sparse.

@codecov-commenter
Copy link

codecov-commenter commented Aug 13, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (3ecd46a) 100.00% compared to head (27d5789) 100.00%.
Report is 1 commits behind head on master.

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff            @@
##            master      #360   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           21        21           
  Lines         2209      2231   +22     
=========================================
+ Hits          2209      2231   +22     
Files Coverage Δ
src/viztracer/cellmagic.py 100.00% <100.00%> (ø)
src/viztracer/decorator.py 100.00% <100.00%> (ø)
src/viztracer/tracer.py 100.00% <100.00%> (ø)
src/viztracer/viztracer.py 100.00% <100.00%> (ø)

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Owner

@gaogaotiantian gaogaotiantian left a comment

Choose a reason for hiding this comment

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

The current logic has became a bit hard to follow. I made a structural suggestion for the current logic, feel free to discuss it if you disagree.

src/viztracer/cellmagic.py Outdated Show resolved Hide resolved
src/viztracer/decorator.py Outdated Show resolved Hide resolved
@gaogaotiantian
Copy link
Owner

Hi @AlexeyKozhevin , are you still interested in working on this PR? I can take over if you think this is too much work. I think the PR still has its value and I'd like to merge it once it's polished.

@AlexeyKozhevin
Copy link
Contributor Author

Hi, unfortunately, I haven't had enough time lately, but I expect to get back to the task in the next two weeks. If anything changes, I will post. Thank you very much for your interest in PR

@gaogaotiantian
Copy link
Owner

Hi, unfortunately, I haven't had enough time lately, but I expect to get back to the task in the next two weeks. If anything changes, I will post. Thank you very much for your interest in PR

Sounds good! Thanks for your time. Python 3.12.0 is coming soon so I plan to update viztracer to 0.16.0 (there are a lot of payloads for the version now) and support 3.12.

Copy link
Owner

@gaogaotiantian gaogaotiantian left a comment

Choose a reason for hiding this comment

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

Thanks for working on this.

Seems like you forgot to add the newly added file? I'm not sure if we want a new file and a new class. Is it possible to implement the shield logic in the function directly with contextmanager? I would expect the logic to be rather straightforward.

Also, you made it possible to pass arguments to %%viztracer, which is great. The user does not know that unfortunately. Could you add something in the docs, after the jupyter part, just mention that some of the command line arguments are supported, and list the supported arguments? Thanks!

Other than that, I think the current code looks good.

@AlexeyKozhevin
Copy link
Contributor Author

AlexeyKozhevin commented Oct 24, 2023

Sorry, I really forgot one file. Thank you for comments, I've updated basic_usage.rst.
I leave VizShield in a separate file because the very similar and small VizEvent is in a separate file. The only difference I see is that VizShield is not for users in general. Perhaps they should be put in the same file with context managers?

@gaogaotiantian
Copy link
Owner

gaogaotiantian commented Oct 30, 2023

Sorry, I really forgot one file. Thank you for comments, I've updated basic_usage.rst. I leave VizShield in a separate file because the very similar and small VizEvent is in a separate file. The only difference I see is that VizShield is not for users in general. Perhaps they should be put in the same file with context managers?

I don't think we should have a VizShield class. The shield feature will only used in VizTracer class and it should be so simple with setignorestackcounter(). The current structure is over-complicated. VizEvent might not be the best design, but the user can directly use it.

I think the shield code can be as simple as

from contextlib import contextmanager
...
@contextmanager
def shield_ignore(self):
    prev_ignore_stack = self.setignorestackcounter(0)
    try:
        yield
    finally:
        self.setignorestackcounter(prev_ignore_stack)

I don't think you need to care about whether the tracer is enabled or in log_sparse mode, shield is shield - the caller is responsible for checking the environment. Also you imported VizShield in a file that does not use it which caused a lint error. Should be trivial to fix.

@gaogaotiantian
Copy link
Owner

One final last thing and we are done. Could you move from contextlib import contextmanager before from typing import Any, Callable, Dict, Optional, Sequence, Tuple, Union? Or, feel free to do import contextlib and use contextlib.contextmanager if that's what you prefer.

We are grouping import XX and from XX import YY separately which is what stdlib does (mostly).

@AlexeyKozhevin
Copy link
Contributor Author

I conducted more detailed testing and ran local tests. Unfortunately, I have to return to _VizShield decorator due to additional redundant calls.

At the last commit implementation we need to compensate stack of calls by adding or subtracting of 2 in setignorestackcounter and by adding 1 in the __enter__ of _VizShield (see comments in the code)

In the case of using @contextlib we have to work with additional generator and next calls (see image below). The generator makes the logic of calls compensation not so straightforward and I haven't figured out how to handle it (just increasing the setignorestackcounter parameter doesn't work).

Screenshot 2023-10-31 at 22 39 20

I concluded that syntactic sugar of contexlib makes it difficult to understand what shield_ignore does and stopped at the current implementation. What do you think about it?

Also wondering what you think about the _VizShield place? I haven't found any places where classes are defined locally, but here the class is purely disposable, so I thought it would be appropriate to define it inside shield_ignore

@gaogaotiantian
Copy link
Owner

Ah, right. The with command is actually a series of calls before/after the statement. In that case, I'd say just get rid of the with command. The compensation is easier to understand with the class implementation, but still not easy enough - it's fragile.

Let's do

def shield_ignore(self, func, *args, **kwargs):
    prev_counter = self.setignorestackcounter(0)
    func(*args, **kwargs)
    self.setignorestackcounter(prev_counter)

Could you write a test to make sure setignorestackcounter is not logged in the trace? Basically a test that would fail if you did not do the compensation in setignorestackcounter correctly - that would be helpful in the future to prevent regression.

@AlexeyKozhevin
Copy link
Contributor Author

Do you mean

    def shield_ignore(self, func, *args, **kwargs):
        prev_ignore_stack = self.setignorestackcounter(0)
        res = func(*args, **kwargs)
        self.setignorestackcounter(prev_ignore_stack)
        return res

and

    return shield_ignore(func, *args, **kwargs))

instead of

with local_tracer.shield_ignore():
    return func(*args, **kwargs)

?

And yes, I will add a test.

@gaogaotiantian
Copy link
Owner

Yes that's what I meant - also makes to code simpler I believe.

@AlexeyKozhevin
Copy link
Contributor Author

AlexeyKozhevin commented Nov 1, 2023

I have extended the tests and also modified the existing test_basic. Since entry["name"] for the function is not just f, but also the path to the module where it is defined, the check in check_func always passed. And it seems that in general the wrong things were checked there :)

@AlexeyKozhevin AlexeyKozhevin changed the title Updates for @log_sparse with contect manager and parameters for %%viztracer Updates for @log_sparse with context manager and parameters for %%viztracer Nov 1, 2023
@gaogaotiantian
Copy link
Owner

Okay now we only have a couple of lint issues - you can install flake8 and mypy in your environment and do make lint to check it quickly. For the cellmagic.py, I don't know what happened there, feel free to figure it out, but if it's too complicated, just do an inline ignore (I think it's # type: ignore? There are a couple in the source files already).

For the decorator, I think _log_sparse_wrapper is guaranteed a not None func right? So removing the Optional for the func argument typing might fix most of the problems.

If you are not familiar with typing, you can send the typing you can't solve here and I'll take a look. Thanks!

@AlexeyKozhevin
Copy link
Contributor Author

AlexeyKozhevin commented Nov 7, 2023

flake8 and mypy say it's okay now.

Unfortunately, I have no idea what is the problem with cellmagic.py so I have to set # type: ignore.

@gaogaotiantian gaogaotiantian merged commit 704005f into gaogaotiantian:master Nov 8, 2023
43 checks passed
@gaogaotiantian
Copy link
Owner

Thank you for your contribution, it's been a long journey and I'm really glad that log_sparse now works with the inline tracer and jupyter!

@AlexeyKozhevin
Copy link
Contributor Author

Thank you for advices and review! It was really interesting.
We already use viztracer in our ML projects in notebook. Some things have become much easier to profile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

log_sparse=True doesn't work with context manager
3 participants