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 — Improve performance of name normalization by jaraco · Pull Request #533 · python/importlib_metadata

Conversation

@jaraco

@jaraco jaraco commented Mar 20, 2026

Copy link
Copy Markdown
Member

Backport of changes from python/cpython#143658

Sorry, something went wrong.

Uh oh!

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

All reactions
hugovk and others added 5 commits March 20, 2026 03:04
@hugovk @henryiii
@picnixz @johnslavik @jaraco
gh-143658: importlib.metadata: Use str.translate to improve perform…
3e7f3ac
…ance of `importlib.metadata.Prepared.normalized` (#143660)

Co-authored-by: Henry Schreiner <[email protected]>
Co-authored-by: Bénédikt Tran <[email protected]>
Co-authored-by: Bartosz Sławecki <[email protected]>
@hugovk @henryiii @jaraco
gh-143658: Use str.lower and replace to further improve performan…
001db0d
…ce of `importlib.metadata.Prepared.normalized` (#144083)

Co-authored-by: Henry Schreiner <[email protected]>
@jaraco
Remove CPython news fragments.
852e44f
@jaraco
Use parameterize fixture for parameterized tests.
1b0be12
@jaraco
Add performance test for Prepared.normalize.
a77d0d1
@jaraco

jaraco commented Mar 20, 2026

Copy link
Copy Markdown
Member Author

I don't understand why the performance test is not showing any effect:

exercises.py:cached distribution: 0:00:00.000169 (+-1 day, 23:59:59.999974, -13%)
exercises.py:discovery: 0:00:00.000173 (+0:00:00, 0%)
exercises.py:entry_points(): 0:00:00.002320 (+0:00:00.000060, 3%)
exercises.py:entrypoint_regexp_perf: 0:00:00.000069 (+-1 day, 23:59:59.999998, -3%)
exercises.py:normalize_perf: 0:00:00 (+0:00:00, 0%)
exercises.py:uncached distribution: 0:00:00.000273 (+-1 day, 23:59:59.999978, -7%)

It's seeming to indicate that the normalize('sample') runs in 0 time.

If I add a sleep, it does reveal some performance degradation:

diff --git a/exercises.py b/exercises.py
index b346cc05f8..a48c57fe6e 100644
--- a/exercises.py
+++ b/exercises.py
@@ -49,6 +49,8 @@ def entrypoint_regexp_perf():
 
 def normalize_perf():
     # python/cpython#143658
+    import time
     import importlib_metadata  # end warmup
 
+    time.sleep(0.001)
     importlib_metadata.Prepared.normalize('sample')
exercises.py:normalize_perf: 0:00:00.001270 (+0:00:00, 0%)

So maybe the operation is too small for timeit to measure?

All reactions

Sorry, something went wrong.

Uh oh!

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

@jaraco

jaraco commented Mar 20, 2026

Copy link
Copy Markdown
Member Author

Running timeit directly reveals some run time:

 🐚 .tox/py/bin/python -m timeit --setup 'import importlib_metadata' -- 'importlib_metadata.Prepared("sample")'
1000000 loops, best of 5: 231 nsec per loop

Aah - so 231 nsec is smaller than the precision of Python's timespan, which only handles microseconds.

 importlib_metadata backport-cpython-143658 🐚 pip-run tempora
Python 3.14.3 (main, Feb  3 2026, 15:32:20) [Clang 17.0.0 (clang-1700.6.3.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import tempora
>>> tempora.parse_timedelta('231 nsec')
datetime.timedelta(0)
All reactions

Sorry, something went wrong.

Uh oh!

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

@jaraco jaraco mentioned this pull request Mar 20, 2026
Better support for sub-microsecond operations jaraco/pytest-perf#18
Closed
@jaraco
Repeat the operation to get performance visibility.
cbadafc
@jaraco

jaraco commented Mar 20, 2026 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Member Author

The latest benchmark shows a 74% reduction in execution time.

exercises.py:normalize_perf: 0:00:00.000100 (+-1 day, 23:59:59.999722, -74%)

I get different numbers when I run timeit manually (~230ns vs ~510ns, 55% reduction).

Was it worth it to save a few hundred nanoseconds?

All reactions

Sorry, something went wrong.

Uh oh!

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

@jaraco jaraco mentioned this pull request Mar 20, 2026
importlib.metadata: Use str.translate to improve performance of importlib.metadata.Prepared.normalized python/cpython#143658
Closed
@hugovk

hugovk commented Mar 20, 2026 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Member

In many cases, the speedup won't make any noticeable effect. The change is similar to an improvement from packaging, which was does have real benefits for pip when doing large resolutions (https://iscinumpy.dev/post/packaging-faster/).

Let's also ask if @henryiii is aware of any use cases.

Personally, I would make this change, I don't think it makes the code harder to read or maintain. But it's fine if you'd prefer to reject this and/or revert in CPython.


btw I'd already opened #529 against main, and this is against maint/8.x. I couldn't find a contrib guide, what's the normal workflow? Can this be documented somewhere, or did I miss it?

This is the better PR -- same prod code change, but parametrised tests and a performance benchmark added.

All reactions

Sorry, something went wrong.

Uh oh!

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

@jaraco

jaraco commented Mar 20, 2026

Copy link
Copy Markdown
Member Author

btw I'd already opened #529 against main, and this is against maint/8.x. I couldn't find a contrib guide, what's the normal workflow? Can this be documented somewhere, or did I miss it?

Thanks for that PR; I'd just not gotten to it as my attention/bandwidth are limited.

There's some documentation in https://github.com/python/importlib_metadata/wiki/Development-Methodology. And the contribution to main was the correct thing at the time. But because I'm now getting this change in place (along with some others) and there's a 9.0 release that I'd like to exclude for the time being, that's why I'm targeting 8.x.

hugovk 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.

@jaraco
Move behavior description into the docstring. Remove references to in…
27169dc
…termediate implementations. Reference the rationale.
@jaraco

jaraco commented Mar 20, 2026

Copy link
Copy Markdown
Member Author

Personally, I would make this change, I don't think it makes the code harder to read or maintain. But it's fine if you'd prefer to reject this and/or revert in CPython.

I would argue it is less readable and has other concerns.

It introduces a new value variable whose state set in multiple locations. I try to avoid mutated variables and follow a functional paradigm wherever possible. In fact, I'm tempted to wrap the while loop in another function replace_all in order to encapsulate these imperative behaviors.

It also repeats itself, using the .replace operation an arbitrary number of times both statically and dynamically.

I'd also argue that it's less sophisticated, using lower-level operations instead of a more succinct and reusable approach. Although regex comes with its own pitfalls, it elevates the conversation by re-using well-known (and documented) approaches.

Additionally, although the spec is "PEP 503 normalization plus dashes as underscores", it doesn't actually perform PEP 503 normalization, but compiles in the aggregate operation to produce an equivalent output. Compare that with the original implementation, where the code reflected precisely the specified behavior, even though it could have simply subbed to _ and eliminated the .replace, making the code more self-documenting.


These concerns are mostly just me being pedantic, but I wanted to articulate why I would prefer the prior implementation absent any performance concerns, and why I'd prefer we get a strong rationale for the benefits. Still, I missed my opportunity to get my review in before merged with CPython, so I'm planning to proceed with this change to get the code bases aligned and we can consider reverting later (while keeping the performance and unit tests).

hugovk 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.

@jaraco
jaraco merged commit 8c5d91b into maint/8.x Mar 20, 2026
15 of 30 checks passed

Uh oh!

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

@jaraco
jaraco deleted the backport-cpython-143658 branch March 20, 2026 16:06
This was referenced Mar 20, 2026
Use str.translate to improve performance of normalize #529
Closed
gh-146228: Better fork support in cached FastPath python/cpython#146231
Merged
This was referenced Jul 13, 2026
Add parse_nanoseconds for sub-microsecond duration parsing jaraco/tempora#49
Merged
Compare timings in nanoseconds for sub-microsecond precision jaraco/pytest-perf#23
Merged
Add Duration, a nanosecond-resolution timedelta jaraco/tempora#50
Merged
Simplify normalize_perf now that pytest-perf resolves sub-microsecond timings #539
Closed
Consider reverting optimization in normalization #540
Closed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

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.

2 participants

@jaraco @hugovk
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.