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

Improve include time of Mojo::Base by extracting monkey_patch #2173

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

okurz
Copy link

@okurz okurz commented Apr 5, 2024

Motivation

Commit 7e9a2ad introduced a "require Mojo::Util" causing a significant chain of further dependencies being pulled in which IMHO should be avoided for the very base module which is in particular being advertised as useful for just enabling strictures and common import checks.

Summary

This commit moves out the function "monkey_patch" into its own module to break or prevent the circular dependency between Mojo::Base and Mojo::Util.

With that the import of Mojo::Base is more efficient, time perl -e 'use Mojo::Base
on my system reduced from 224±12.08 ms to 52.0±2.3 ms which I consider a considerable improvement for Mojo::Base which is used as a baseclass in many cases.

lib/Mojo/Util.pm Outdated Show resolved Hide resolved
lib/Mojo/Base.pm Outdated Show resolved Hide resolved
@okurz okurz force-pushed the feature/optimize_mojo_base branch from c408463 to 949dbaa Compare April 5, 2024 19:44
@okurz

This comment was marked as resolved.

@Grinnz
Copy link
Contributor

Grinnz commented Apr 5, 2024

  • Addressed CI failure "Undefined subroutine &Mojo::Util::class_to_path" by moving class_to_path to Mojo::Base and adjusting the rest of the repository accordingly, re-exporting from Mojo::Util

This should not be done. This creates a method in all classes using Mojo::Base as a base.

@Grinnz
Copy link
Contributor

Grinnz commented Apr 5, 2024

Additionally, if Mojo::Util re-exports class_to_path no other adjustment should be necessary.

@okurz

This comment was marked as resolved.

@okurz
Copy link
Author

okurz commented Apr 5, 2024

  • Addressed CI failure "Undefined subroutine &Mojo::Util::class_to_path" by moving class_to_path to Mojo::Base and adjusting the rest of the repository accordingly, re-exporting from Mojo::Util

This should not be done. This creates a method in all classes using Mojo::Base as a base.

done, updated

@Grinnz
Copy link
Contributor

Grinnz commented Apr 5, 2024

BaseUtil seems reasonable and self-explanatory to me, but there may be other opinions. As long as Mojo::Util imports and re-exports the functions from there correctly, there should be no changes needed to other modules.

@okurz
Copy link
Author

okurz commented Apr 5, 2024

BaseUtil seems reasonable and self-explanatory to me, but there may be other opinions. As long as Mojo::Util imports and re-exports the functions from there correctly, there should be no changes needed to other modules.

Sure, let's see how that looks. Added a commit to combine monkey_patch and class_to_path in Mojo::BaseUtil and reverted changes in other modules to use still the functions as exported from Mojo::Util.

lib/Mojo/BaseUtil.pm Outdated Show resolved Hide resolved
lib/Mojo/BaseUtil.pm Outdated Show resolved Hide resolved
@Grinnz
Copy link
Contributor

Grinnz commented Apr 5, 2024

IMO, t/mojo/util.t should remain unchanged, to ensure there is no change in compatibility for the re-exported functions. t/mojo/base_util.t may be unnecessary if so.

@kraih
Copy link
Member

kraih commented Apr 5, 2024

I'd be interested in some real world benchmarks where this makes a difference.

@poti1
Copy link

poti1 commented May 7, 2024

I'm looking forwards to seeing this hopefully soon in the library :)

@poti1
Copy link

poti1 commented May 7, 2024

By simply copying over monkey_patch and the set_subname declarations from Mojo::Util to my cli tool (and removing use/require Mojo::Util), I saw these results in performance improvement:

BEFORE:
real 0m0.179s
user 0m0.101s
sys 0m0.052s

NOW:
real 0m0.045s
user 0m0.018s
sys 0m0.015s

When running on my phone's termux, the change of those values is really felt.

I compared before and after also with Nut Prof:

BEFORE:
Profile of -e for 198ms (of 205ms), executing 15207 statements and 6061 subroutine calls in 76 source files and 27 string evals.

AFTER:
Profile of -e for 11.7ms (of 11.8ms), executing 144 statements and 57 subroutine calls in 8 source files.

lib/Mojo/BaseUtil.pm Outdated Show resolved Hide resolved
@okurz okurz force-pushed the feature/optimize_mojo_base branch from 1942558 to a8ba9a5 Compare May 8, 2024 10:10
@okurz

This comment was marked as outdated.

@kraih kraih requested review from a team, marcusramberg, kraih, jhthorsen and jberger May 8, 2024 10:27
@okurz okurz force-pushed the feature/optimize_mojo_base branch from a8ba9a5 to b7002c7 Compare May 8, 2024 14:12
@okurz

This comment was marked as outdated.

@kraih
Copy link
Member

kraih commented May 8, 2024

Please squash commits.

@okurz okurz force-pushed the feature/optimize_mojo_base branch from b7002c7 to 597c50b Compare May 8, 2024 17:20
@okurz
Copy link
Author

okurz commented May 8, 2024

Please squash commits.

done

lib/Mojo/BaseUtil.pm Outdated Show resolved Hide resolved
t/mojo/util.t Outdated Show resolved Hide resolved
lib/Mojo/Util.pm Outdated Show resolved Hide resolved
@okurz okurz force-pushed the feature/optimize_mojo_base branch from 597c50b to b200779 Compare May 8, 2024 18:54
@okurz
Copy link
Author

okurz commented May 8, 2024

Covered all last three open review comments

Grinnz
Grinnz previously approved these changes May 8, 2024
Copy link
Contributor

@Grinnz Grinnz left a comment

Choose a reason for hiding this comment

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

Looks good to me

use Pod::Usage qw(pod2usage);
use Socket qw(inet_pton AF_INET6 AF_INET);
use Sub::Util qw(set_subname);
Copy link
Member

Choose a reason for hiding this comment

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

Do you think anyone depends on Mojo::Util::set_subname? They probably shouldn't but they could

Copy link
Member

Choose a reason for hiding this comment

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

It isn't documented and the fix is trivial so I don't think I care at all.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would not consider use of any subroutines imported without a documented interface as our responsibility.

Copy link
Member

Choose a reason for hiding this comment

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

No documentation, no feature.


subtest 'monkey_patch' => sub {
{

Copy link
Member

Choose a reason for hiding this comment

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

nitpick but blank line?

Copy link
Author

Choose a reason for hiding this comment

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

ok, done. Included is a second commit removing redundant blank lines in the added code as well as where I originally copied the test code from.

Copy link
Author

Choose a reason for hiding this comment

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

Nope, perltidy actually demands the blank line :D Reverted the last change

@jhthorsen
Copy link
Member

I think the name Mojo::BaseUtil is a bit awkward. How to tell what is in which package? I can't come up with a killer name, but I'm thinking about Mojo::CoreUtil or Mojo::PureUtil, which might indicate that the functions in that package is without any significant dependencies? I guess Mojo::BaseUtil makes sense, if we only have functions that Mojo::Base depends on, but I wonder if it's a slippery slope...

Should any of the following functions be moved over? The reason I ask, is because maybe it makes sense to move class_to_file(), when class_to_path() was moved.

  • camelize()
  • class_to_file()
  • decamelize()

@Grinnz
Copy link
Contributor

Grinnz commented May 9, 2024

While that might make sense for code level consistency, I think it's beneficial to keep the module limited in scope to its stated purpose. All of these functions should continue to be used from Mojo::Util in normal cases. It can be viewed less as "moving" the functions from the user's POV because they continue to be re-exported.

@jhthorsen
Copy link
Member

Then I think that should be stated in the documentation, meaning that functions can be added and removed in the future to keep Mojo::Base quick to load.

@Grinnz
Copy link
Contributor

Grinnz commented May 9, 2024

It could make sense to me to remove the function documentation from the new module (these would need to be added as trusted in t/pod_coverage.t), and document that it provides functions for use in Mojo::Base and shouldn't be depended on to remain compatible.

@okurz
Copy link
Author

okurz commented May 9, 2024

It could make sense to me to remove the function documentation from the new module

ok, fine for me. As there was already one "+1" I followed that suggestion and included that change

Also included is a second commit removing redundant blank lines in the added code as well as where I originally copied the test code from.

@poti1
Copy link

poti1 commented May 9, 2024

For my own cli script, I ended up doing this to avoid loading in thousands of subs right away:

sub monkey_patch {
    my ( $class, %patch ) = @_;
    require Sub::Util;
    for ( keys %patch ) {
        *{"${class}::$_"} =
          Sub::Util::set_subname( "${class}::$_", $patch{$_} );
    }
}

Commit 7e9a2ad introduced a "require Mojo::Util" causing a significant
chain of further dependencies being pulled in which IMHO should be
avoided for the very base module which is in particular being advertised
as useful for just enabling strictures and common import checks.

This commit moves out the function "monkey_patch" into its own module to
break or prevent the circular dependency between Mojo::Base and
Mojo::Util.

With that the import of Mojo::Base is more efficient,
`time perl -e 'use Mojo::Base`
on my system reduced from 224±12.08 ms to 52.0±2.3 ms which I consider a
considerable improvement for Mojo::Base which is used as a baseclass in
many cases.

Further minor changes included:
* Directly require MonkeyPatch for cleaner subclassing + POD
* Correct use of MonkeyPatch with empty import
* Combine monkey_patch and class_to_path in new Mojo::BaseUtil
@okurz okurz force-pushed the feature/optimize_mojo_base branch from 3fa30df to ef789ba Compare May 9, 2024 17:14

our @EXPORT_OK = (qw(class_to_path monkey_patch));


Copy link
Member

Choose a reason for hiding this comment

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

Nitpick: Extra newline.

L<Mojo::BaseUtil> provides a C<class_to_path> and the C<monkey_patch> function
for L<Mojo>. The main purpose is to provide functions to both C<Mojo::Base>
and C<Mojo::Util> so that C<Mojo::Base> does not have to load the rest of
C<Mojo::Util>.
Copy link
Member

Choose a reason for hiding this comment

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

Nitpick: Not 120 char lines.

use Mojo::BaseUtil qw(class_to_path monkey_patch);


subtest 'class_to_path' => sub {
Copy link
Member

Choose a reason for hiding this comment

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

Nitpick: Extra newline.

@@ -11,4 +11,7 @@ my @await = (
qw(AWAIT_NEW_FAIL AWAIT_ON_CANCEL AWAIT_ON_READY AWAIT_WAIT)
);

all_pod_coverage_ok({also_private => ['BUILD_DYNAMIC', @await, 'spurt']});
# base utils only to be used in Mojo::Base and should not be used elsewhere
Copy link
Member

Choose a reason for hiding this comment

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

Nitpick: Comment capitalisation.

Copy link
Member

Choose a reason for hiding this comment

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

Is that correct english? Sounds a little wrong to me.

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

Successfully merging this pull request may close these issues.

None yet

8 participants