Thanks to visit codestin.com
Credit goes to github.com

Skip to content

MAINT: import time: avoid repeated textwrap function dispatch instantiation #14095

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

Merged
merged 3 commits into from
Jul 26, 2019

Conversation

hmaarrfk
Copy link
Contributor

Avoid the use of textwrap to deindent static code. This shaves about 6-8ms ms of import time (at the expense of slightly less readable code depending on who you ask -- but definitely if you ask @eric-wieser)

After: 99.1±2ms
Before: 106±2ms (master)

numpy/ma/core.py Outdated
""")
long_std="""\
masked_%(name)s(data =
%(data)s,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

formatting definitely doesn't look right. give me a second.

@hmaarrfk hmaarrfk force-pushed the import_time_textwrap branch from bf780b8 to fa60c30 Compare July 24, 2019 02:40
@eric-wieser
Copy link
Member

eric-wieser commented Jul 24, 2019

I'd be curious to know how much of this is the cost of importing textwrap, and how much is the cost of invoking dedent. I assume the stats you give above do not include the benefit of removing an import-time dependency on re?

@hmaarrfk
Copy link
Contributor Author

The stats I gave were obtained from running

asv continuous -b time_numpy  master import_time_textwrap -E conda:3.7

with this branch including only this one commit. As such, the code is still improting re elsewhere.

A runtime dependency on re is very difficult to remove. Let me try to find a concrete example.

@eric-wieser
Copy link
Member

Let me try to find a concrete example.

No need, just wanted to confirm that this timing is not caused by removing it.

In the interest of breaking down the import cost here, can you leave behind the import textwrap but not actually use it, and see how that import time compares?

@hmaarrfk
Copy link
Contributor Author

yeah, i just looked at the textwrap source, it seems pretty trivial, and shouldn't be a contributor, let me test.

@hmaarrfk
Copy link
Contributor Author

It seems like it is the fact that we are using dedent that is causing the slowdown

$ asv continuous -b time_numpy  master import_time_textwrap -E conda:3.7
· `wheel_cache_size` has been renamed to `build_cache_size`. Update your `asv.conf.json` accordingly.
· No executable found for python 3.6
· Creating environments
· Discovering benchmarks
·· Uninstalling from conda-py3.7-six
·· Installing 690132df <import_time_textwrap> into conda-py3.7-six.
· Running 6 total benchmarks (2 commits * 1 environments * 3 benchmarks)
[  0.00%] · For numpy commit ea965e4c <master> (round 1/2):
[  0.00%] ·· Building for conda-py3.7-six.......
[  0.00%] ·· Benchmarking conda-py3.7-six
[  8.33%] ··· Running (bench_import.Import.time_numpy--)...
[ 25.00%] · For numpy commit 690132df <import_time_textwrap> (round 1/2):
[ 25.00%] ·· Building for conda-py3.7-six.
[ 25.00%] ·· Benchmarking conda-py3.7-six
[ 33.33%] ··· Running (bench_import.Import.time_numpy--)...
[ 50.00%] · For numpy commit 690132df <import_time_textwrap> (round 2/2):
[ 50.00%] ·· Benchmarking conda-py3.7-six
[ 58.33%] ··· bench_import.Import.time_numpy                                                       101±1ms
[ 66.67%] ··· bench_import.Import.time_numpy_inspect                                               104±1ms
[ 75.00%] ··· bench_linalg.Lstsq.time_numpy_linalg_lstsq_a__b_float64                          2.69±0.08ms
[ 75.00%] · For numpy commit ea965e4c <master> (round 2/2):
[ 75.00%] ·· Building for conda-py3.7-six.
[ 75.00%] ·· Benchmarking conda-py3.7-six
[ 83.33%] ··· bench_import.Import.time_numpy                                                       107±2ms
[ 91.67%] ··· bench_import.Import.time_numpy_inspect                                               109±2ms
[100.00%] ··· bench_linalg.Lstsq.time_numpy_linalg_lstsq_a__b_float64                          2.64±0.04ms

BENCHMARKS NOT SIGNIFICANTLY CHANGED.
(numpy) mark@mark-xps ~/g/n/benchmarks import_time_textwrap|+1⚑ 1
$ git log -n 1
commit 690132dfad9ddc775fd4607581050af048c9f84f (HEAD -> import_time_textwrap, origin/import_time_textwrap)
Author: Mark Harfouche <[email protected]>
Date:   Tue Jul 23 22:59:41 2019 -0400

    Import textwrap again just to test

relevant_args = dispatcher(*args, **kwargs)
return implement_array_function(
implementation, {name}, relevant_args, args, kwargs)
""".format(name=implementation.__name__)
Copy link
Member

Choose a reason for hiding this comment

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

I suspect this is where all the cost is - we end up dedenting the same string for every single numpy function, which is obviously expensive. Dedenting it just once wouldn't be nearly as bad, although at that point chances are the string definition lives outside the function, at which point there's nothing to even dedent.

In the interest of preserving the visual indent, I might be inclined to move this string definition to a global _wrapped_func_source variable in the file, which still avoids paying for textwrap

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the interest of preserving the visual indent, I might be inclined to move this string definition to a global _wrapped_func_source variable in the file, which still avoids paying for textwrap

Is this an opinion, or a requirement to get your approval on this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But I do suspect you are right in the source of the problem. I just don't personally see any issue with deindenting strings manually.

Copy link
Member

Choose a reason for hiding this comment

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

Is this an opinion

It's an opinion that probably gates my immediate approval until another maintainer weighs ins.

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'm over it, lets hunt more milliseconds, and work to deprecate the whole testing module. Much more interesting ;)

Copy link
Member

Choose a reason for hiding this comment

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

I have a problem with dedenting strings manually, they are ugly, and ugly is unpleasant to maintain.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

very fair.

@hmaarrfk
Copy link
Contributor Author

@eric-wieser, you know, my original attempt at ripping out re used a ton of lru_cache. I felt like I would never code like that unless I was really trying to shave off milliseconds, and even then, it would end up being in vain since somebody else would end up importing re and the lru_cache would likely make things slower than the original case.

I think in this case, manual deindentenation is an appropriate compromise.

@hmaarrfk hmaarrfk force-pushed the import_time_textwrap branch from e688181 to 71f8cbe Compare July 25, 2019 03:31
@hmaarrfk hmaarrfk changed the title MNT: import time: avoid textwrap in core library MNT: import time: avoid repeated textwrap function dispatch instatiation Jul 25, 2019
@hmaarrfk
Copy link
Contributor Author

@eric-wieser. My milliseconds need this more than my intention to discuss coding style.

Benchmarks show similar improvements, again, hard to say when you are using the computer. Seeing as textwrap is built on top of re, it it likely only adds 1 ms instead of the 6 ms it added before.

Thanks for hunting down the true source of the issue. I won't be affraid to use textwrap in my other projects now.

Copy link
Member

@eric-wieser eric-wieser left a comment

Choose a reason for hiding this comment

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

I could have been persuaded to take the original with some more thought, but this shortcuts that step. Might be worth a comment explaining it's location.

Can you confirm this still gives the savings you needed?

@hmaarrfk hmaarrfk force-pushed the import_time_textwrap branch from 71f8cbe to 3f06982 Compare July 25, 2019 03:38
@hmaarrfk
Copy link
Contributor Author

Yeah, it is still about a 4-6 ms in saving. Pretty good.

@seberg seberg changed the title MNT: import time: avoid repeated textwrap function dispatch instatiation MAINT: import time: avoid repeated textwrap function dispatch instantiation Jul 26, 2019
@seberg
Copy link
Member

seberg commented Jul 26, 2019

I agree, simply moving out the string formatting seems fine for that import time boost, thanks @hmaarrfk. I will assume that even if we do things such as vendoring dedent this will still make sense. So putting it in.

@seberg seberg merged commit 702c357 into numpy:master Jul 26, 2019
@@ -109,6 +109,18 @@ def decorator(func):
return decorator



Copy link
Member

Choose a reason for hiding this comment

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

Opst, empty line too much (don't worry about it though), just noticed that I did not nitpick ;).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Opps

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.

4 participants