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

Avoid overhead of cast() calls when not type checking #2500

Merged
merged 1 commit into from
Aug 7, 2024

Conversation

correctmost
Copy link
Contributor

Type of Changes

Type
βœ“ πŸ”¨ Refactoring

Description

While profiling Pylint with the yt-dlp codebase, I noticed that cast was called 1.277 million times:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  1277992    0.130    0.000    0.130    0.000 /usr/lib/python3.12/typing.py:2173(cast)

This PR avoids 1.1 million of these calls.

Background

python/cpython#116290 (comment) has this to say about cast:

My own vision for call inlining is that it's mostly going to make "trivial" functions disappear -- a good example would be typing.cast(), which returns its second argument unchanged. I currently sometimes hesitate about putting that in because of the perceived overhead of even the most trivial function call [...]

Stats

Before

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  1277992    0.130    0.000    0.130    0.000 /usr/lib/python3.12/typing.py:2173(cast)
Command Mean [s] Min [s] Max [s] Relative
pylint --recursive=y . 36.214 Β± 0.381 35.590 36.608 1.00

After

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   176834    0.022    0.000    0.022    0.000 /usr/lib/python3.12/typing.py:2173(cast)
Command Mean [s] Min [s] Max [s] Relative
pylint --recursive=y . 35.195 Β± 0.165 34.974 35.326 1.00

Reproduction notes

  • The tables were generated by hyperfine
    • hyperfine --ignore-failure --warmup 2 --runs 5 --export-markdown=baseline.md 'pylint --recursive=y .'

Copy link

codecov bot commented Aug 7, 2024

Codecov Report

All modified and coverable lines are covered by tests βœ…

Project coverage is 92.98%. Comparing base (15207a7) to head (26c6840).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2500      +/-   ##
==========================================
- Coverage   92.98%   92.98%   -0.01%     
==========================================
  Files          93       93              
  Lines       11040    11039       -1     
==========================================
- Hits        10266    10265       -1     
  Misses        774      774              
Flag Coverage Ξ”
linux 92.86% <ΓΈ> (-0.01%) ⬇️
pypy 92.98% <ΓΈ> (-0.01%) ⬇️
windows 92.97% <ΓΈ> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Coverage Ξ”
astroid/transforms.py 100.00% <ΓΈ> (ΓΈ)

Copy link
Member

@Pierre-Sassoulas Pierre-Sassoulas left a comment

Choose a reason for hiding this comment

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

Thank you for the merge request and the benchmark. This avoid a lot of cast call but at the cost of the same number of if check. I'm not convinced the net result is positive (as the given benchmark do not include the information regarding the cost of the additionnal 'if' afaiu) reread the summary and the diff is surprisingly large.

@Pierre-Sassoulas Pierre-Sassoulas added this to the 3.4.0 milestone Aug 7, 2024
@correctmost
Copy link
Contributor Author

Thanks for the review :).

The numbers from the hyperfine runs will have some noise because my computer is not perfectly tuned to eliminate variance, the Pylint runs involve disk I/O, etc.

But the cProfile stats and these additional timeit stats make me believe that there is a small speed-up for large codebases:

# Without the if check
python -m timeit -s 'from typing import cast, TYPE_CHECKING; x=1' -n 1000000 'x=cast(int, x)'
1000000 loops, best of 5: 40.1 nsec per loop

# With the if check
python -m timeit -s 'from typing import cast, TYPE_CHECKING; x=1' -n 1000000 'if TYPE_CHECKING: x=cast(int, x)'
1000000 loops, best of 5: 9.63 nsec per loop

@jacobtylerwalls jacobtylerwalls merged commit 0156c04 into pylint-dev:main Aug 7, 2024
22 checks passed
@correctmost correctmost deleted the cm/avoid-casts branch August 7, 2024 12:22
@jacobtylerwalls jacobtylerwalls modified the milestones: 3.4.0, 4.0.0 Sep 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement ✨ Improvement to a component topic-performance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants