GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-118761: Reduce import time of gettext.py by delaying re import by eli-schwartz · Pull Request #128898 · python/cpython

Conversation

@eli-schwartz

@eli-schwartz eli-schwartz commented Jan 16, 2025 •
edited by bedevere-app Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

gettext is often imported in programs that may not end up translating anything. In fact, the struct module already has a delayed import when parsing GNUTranslations to speed up the no .mo files case. The re module is also used in the same situation, but behind a function chain only called by GNUTranslations.

cache the compiled regex globally the first time it is used. The finditer function can be converted to a method call on the compiled object (it always could) which is slightly more efficient and necessary for the conditional re import.

  • Issue: Improve import time of various stdlib modules #118761

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@ghost

ghost commented Jan 16, 2025 •
edited by ghost
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.
CLA signed

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app bedevere-app Bot mentioned this pull request Jan 16, 2025
Improve import time of various stdlib modules #118761
Closed
@bedevere-app bedevere-app Bot added the awaiting review label Jan 16, 2025
@eli-schwartz
eli-schwartz force-pushed the gettext-importtime branch from 44b48bd to 2a4bca6 Compare January 16, 2025 00:49
@eli-schwartz eli-schwartz mentioned this pull request Jan 16, 2025
Speed up small CLI tools by removing import re from the excecutable template pypa/pip#13165
Closed
1 task done
AA-Turner
AA-Turner requested changes Jan 16, 2025
View reviewed changes

@AA-Turner AA-Turner left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Line 51 still runs import re! I can't add a suggestion to remove it.

The only other comment I had is maybe you could preserve the string pattern in a module global and move the compilation to the function, to reduce the diff size?

A

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@bedevere-app bedevere-app Bot added awaiting changes and removed awaiting review labels Jan 16, 2025
@bedevere-app

bedevere-app Bot commented Jan 16, 2025

Copy link
Copy Markdown

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@eli-schwartz

eli-schwartz commented Jan 16, 2025

Copy link
Copy Markdown
Contributor Author

Line 51 still runs import re! I can't add a suggestion to remove it.

tfw you forget to git add -p the entirety of the changes...

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@eli-schwartz

eli-schwartz commented Jan 16, 2025

Copy link
Copy Markdown
Contributor Author

The only other comment I had is maybe you could preserve the string pattern in a module global and move the compilation to the function, to reduce the diff size?

I wonder if that really helps? The diff size isn't bad right now (IMHO), and having an effectively useless module global as a side effect of the pull request process looks a tiny bit odd to me. If you really want it, I could do it though.

AA-Turner reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@eli-schwartz
eli-schwartz force-pushed the gettext-importtime branch from 2a4bca6 to 16cb697 Compare January 17, 2025 00:43
@picnixz

picnixz commented Jan 17, 2025

Copy link
Copy Markdown
Member

Out of curiosity, how are the hyperfine and -X importtime benchmarks?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@eli-schwartz

eli-schwartz commented Jan 17, 2025

Copy link
Copy Markdown
Contributor Author

It should be fairly good -- this not only avoids a re import, it avoids compiling a regex during import. Unfortunately I can't answer the question... because I just tried testing it and hyperfine reports that "statistical outliers were detected" and wildly different timings across repeated hyperfine runs (including, some hyperfine runs for this PR that were much slower than some runs without this PR). Closing most programs other than my system's Window Manager didn't help reduce the bias.

I'm not really experienced with running benchmarks, so maybe there's a trick I'm simply not aware of.

If someone else wants to run their own timings I'll happily accept them at face value and include them in the news file though.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@picnixz

picnixz commented Jan 17, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

I'm not really experienced with running benchmarks, so maybe there's a trick I'm simply not aware of.

The easiest way is to:

$  ./python -X importtime -c 'import gettext'

and report the numbers only for gettext. For instance, if re is already imported, this wouldn't help. You can also check ./python -I -X importtime -c 'import gettext' and compare as well. Then with hyperfine is used as follows:

$ hyperfine --warmup 8 "./python -c 'import gettext'"

You should compare the numbers against main and this PR, on a RELEASE build at least (possibly with PGO, it doesn't really matter here I think). For the statistical outliers, just ignore them. The reason is that interpreter's startup is sometimes taking more or less time. Just seeing the mean and standard deviation is sufficient in general to decide whether an improvement is noticeable or not.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@eli-schwartz

eli-schwartz commented Jan 17, 2025

Copy link
Copy Markdown
Contributor Author

What I mean is that the timings vary between saying the old code is faster and the new code is faster. But here's an optimistic run I suppose. Maybe we can just assume the most favorable results are true?

$ LD_LIBRARY_PATH=$PWD hyperfine --warmup 8 "./python -c 'import gettext_old'" "./python -c 'import gettext_new'"
Benchmark 1: ./python -c 'import gettext_old'
  Time (mean ± σ):      17.9 ms ±   5.2 ms    [User: 14.5 ms, System: 3.3 ms]
  Range (min … max):    15.0 ms …  41.1 ms    163 runs
 
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
 
Benchmark 2: ./python -c 'import gettext_new'
  Time (mean ± σ):      11.1 ms ±   3.7 ms    [User: 8.7 ms, System: 2.3 ms]
  Range (min … max):     9.5 ms …  28.3 ms    282 runs
 
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
 
Summary
  ./python -c 'import gettext_new' ran
    1.61 ± 0.72 times faster than ./python -c 'import gettext_old'

Results produced by reasoning that the changes are only to a single .py file, and copying the file twice so I can try importing either the old or the new one alternately. No need to checkout different branches.

picnixz reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@tomasr8

tomasr8 commented Jan 17, 2025

Copy link
Copy Markdown
Member

I'm getting similar results as @eli-schwartz (on a release build)

# Old
$ hyperfine --warmup 8 "./python -c 'import gettext'"
Benchmark 1: ./python -c 'import gettext'
  Time (mean ± σ):      23.7 ms ±   4.5 ms    [User: 17.9 ms, System: 5.8 ms]
  Range (min … max):    18.1 ms …  31.9 ms    100 runs
# New
$ hyperfine --warmup 8 "./python -c 'import gettext'"
Benchmark 1: ./python -c 'import gettext'
  Time (mean ± σ):      17.5 ms ±   4.7 ms    [User: 12.8 ms, System: 4.6 ms]
  Range (min … max):    12.1 ms …  29.5 ms    129 runs
picnixz reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@picnixz

picnixz commented Jan 17, 2025

Copy link
Copy Markdown
Member

Can we have the -X importtime benchmarks for the NEWS entry as well? (my other PRs reported the numbers according to -X importtime as hyperfine benchmarks were more to check that everything was indeed faster, but -X importtime would give more precise timings of the import X statement itself).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@tomasr8

tomasr8 commented Jan 17, 2025

Copy link
Copy Markdown
Member

Here you go ;)

      import time: self [us] | cumulative | imported package
(Old) import time:      1070 |       6935 | gettext
(New) import time:       474 |        810 | gettext
picnixz reacted with heart emoji
All reactions
  • ❤️ 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@eli-schwartz

eli-schwartz commented Jan 17, 2025

Copy link
Copy Markdown
Contributor Author
import time:      1704 |       7456 | gettext_old
import time:       425 |        704 | gettext_new
tomasr8 reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@picnixz

picnixz commented Jan 17, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

That's a definite improvement indeed. It will also help other modules such as argparse that import gettext for translation. For CLIs, that will be a nice improvement IMO. Thanks a lot! You can amend the NEWS entry to say that it's now 10x faster (I would just pick up the same formulation as we did in #128858 or #128736) and say that re is no more implicitly exposed as gettext.re (although imported modules should not be accessible, tests that mock the imports for whatever reasons would be happy to know about it)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gpshead
gpshead approved these changes Jan 18, 2025
View reviewed changes
@bedevere-app bedevere-app Bot added awaiting merge and removed awaiting changes labels Jan 18, 2025
@eli-schwartz
pythongh-118761: Reduce import time of gettext.py by delaying re import
fd471b8
gettext is often imported in programs that may not end up translating
anything. In fact, the `struct` module already has a delayed import when
parsing GNUTranslations to speed up the no .mo files case. The re module
is also used in the same situation, but behind a function chain only
called by GNUTranslations.

cache the compiled regex globally the first time it is used. The
finditer function can be converted to a method call on the compiled
object (it always could) which is slightly more efficient and necessary
for the conditional re import.
@eli-schwartz
eli-schwartz force-pushed the gettext-importtime branch from 16cb697 to fd471b8 Compare January 19, 2025 05:02
@eli-schwartz

eli-schwartz commented Jan 19, 2025

Copy link
Copy Markdown
Contributor Author

It will also help other modules such as argparse that import gettext for translation. For CLIs, that will be a nice improvement IMO.

Yup, that was my original motivation in fact. :)

You can amend the NEWS entry to say that it's now 10x faster (I would just pick up the same formulation as we did in #128858 or #128736) and say that re is no more implicitly exposed as gettext.re (although imported modules should not be accessible, tests that mock the imports for whatever reasons would be happy to know about it)

Done.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

AA-Turner
AA-Turner approved these changes Jan 19, 2025
View reviewed changes
@AA-Turner
AA-Turner merged commit c9c9fcb into python:main Jan 20, 2025
@bedevere-app bedevere-app Bot removed the awaiting merge label Jan 20, 2025
@eli-schwartz
eli-schwartz deleted the gettext-importtime branch January 20, 2025 00:09
srinivasreddy pushed a commit to srinivasreddy/cpython that referenced this pull request Jan 21, 2025
@eli-schwartz @srinivasreddy
pythongh-118761: Improve the import time of gettext (python#128898)
f1315a6
``gettext`` is often imported in programs that may not end up translating
anything. In fact, the ``struct`` module already has a delayed import when
parsing ``GNUTranslations`` to speed up the no ``.mo`` files case. The re module
is also used in the same situation, but behind a function chain only
called by ``GNUTranslations``.

Cache the compiled regex globally the first time it is used. The
finditer function is converted to a method call on the compiled
object which is slightly more efficient, and necessary for the
delayed re import.
@eli-schwartz eli-schwartz mentioned this pull request Feb 9, 2025
gh-118761: Always lazy import re in locale #129860
Merged
@dangotbanned dangotbanned mentioned this pull request May 10, 2025
chore: Bump version manually only in pyproject.toml narwhals-dev/narwhals#2514
Merged
3 of 10 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@gpshead gpshead gpshead approved these changes
@AA-Turner AA-Turner AA-Turner approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

5 participants

@eli-schwartz @picnixz @tomasr8 @gpshead @AA-Turner
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.