Skip to content

perf: Add fast path for parsing simple versions (digits and dots only)#1082

Merged
henryiii merged 7 commits intopypa:mainfrom
notatallshaw:add-fast-path-for-parsing-simple-versions
Feb 11, 2026
Merged

perf: Add fast path for parsing simple versions (digits and dots only)#1082
henryiii merged 7 commits intopypa:mainfrom
notatallshaw:add-fast-path-for-parsing-simple-versions

Conversation

@notatallshaw
Copy link
Member

This is a performance trade off, it fast paths versions only containing digits and dots (e.g. 1.2.3), at a slight cost to versions which are more complex (include any dev/pre/post/local components).

Testing on Python 3.14 on my machine and using these versions:

Simple: 1.0.0, 2.1.3, 0.0.1, 1.2.3, 2021.1.1, 1.2.3.4.5.6.7.8, 3.8.0, 1.0, 2.0, 10.5.3
Non-simple: 1.0.0a1, 2.0.0b2, 3.0.0rc1, 1.0.0.post1, 1.0.0.dev1, 1.0.0a1.dev1

I see simple versions go from an median of 1.16 µs to 0.49 µs (2.37x [faster]) and non-simple versions go from a median of 1.16 µs to 1.38 µs (0.84x [slower]).

Probably could do with a wider range of performance testing before accepting..

@notatallshaw notatallshaw force-pushed the add-fast-path-for-parsing-simple-versions branch from c9db103 to 1f64976 Compare February 8, 2026 16:26
@notatallshaw
Copy link
Member Author

@henryiii I think you mentioned you have some script that parses every version from PyPI? I expect this branch to be about ~2x faster, will be interesting to see real world trade off of simple vs. non-simple versions.

@notatallshaw
Copy link
Member Author

notatallshaw commented Feb 10, 2026

I realized for the fast path simple version we can drop the regex and just directly do:

tuple(map(int, version.strip().split(".")))

My quick benchmarking on Python 3.14 shows doing this and catching the exception if it fails speeds up simple version parsing by 4.2x [faster], and slows down non-simple parsing 0.73x [slower], compared to main.

So I came up with a fairly fast check using a frozenset that limited the overhead cost for non-simple versions, with this simple version parsing is sped up by 3.15x [faster], but the slow down in non-simple parsing is only 0.95x [slower], compared to main!

@henryiii
Copy link
Contributor

henryiii commented Feb 10, 2026

FYI, I tried pretty much exactly this (probably still in a stash), and did see it was faster, but decided to work on over all performance of the regex and didn't come back to it, largely due to worries about the non-fast case getting slower. But it looks like you have that impact pretty small (for a nice fast-path improvement). I can check on the full version list soon(ish).

(Edit: "this" being the 4.2x, 0.73x one)

@henryiii
Copy link
Contributor

henryiii commented Feb 10, 2026

$ uv run tasks/benchmark_versions.py
Counter({'release': 7086247, 'pre': 532193, 'dev': 451268, 'post': 97508, 'invalid': 5422, 'epoch': 1155, 'local': 6})
Loaded 8,168,377 versions
Time: 9.9128 seconds
Per version: 0.242712358 µs

$ gh co 1082
$ uv run tasks/benchmark_versions.py
Counter({'release': 7086247, 'pre': 532193, 'dev': 451268, 'post': 97508, 'invalid': 5422, 'epoch': 1155, 'local': 6})
Loaded 8,168,377 versions
Time: 4.3251 seconds
Per version: 0.105899591 µs

$ git stash apply
$ uv run tasks/benchmark_versions.py
Counter({'release': 7086247, 'pre': 532193, 'dev': 451268, 'post': 97508, 'invalid': 5422, 'epoch': 1155, 'local': 6})
Loaded 8,168,377 versions
Time: 4.3296 seconds
Per version: 0.106007648 µs

Final version is with the diff above, using _SIMPLE_VERSION_INDICATORS = frozenset("1234567890.") and issuperset.

@henryiii
Copy link
Contributor

henryiii commented Feb 10, 2026

version.replace(".","").isdecimal() is slightly faster for me, at 4.23 seconds. It does include this oddity, though, which your version also supports, but mine does not:

>>> a = '٠١٢.٣٤٥.٦٧٨٩'
>>> a.replace(".", "").isdecimal()
True
>>> tuple(map(int, a.split(".")))
(12, 345, 6789)

@notatallshaw
Copy link
Member Author

notatallshaw commented Feb 10, 2026

@henryiii is that testing distinct versions or all versions for every project regardless of duplication?

Either way I think this approach catches a nice balance, all your work on speeding up the general case isn't too badly impacted because which ever of these tests we choose then overhead is low enough.

I want to benchmark the overhead across a number of Python versions to make sure there's not an unexpected regression.

@henryiii
Copy link
Contributor

It's every version from PyPI, not deduplicated.

@notatallshaw
Copy link
Member Author

I think this is ready, I tried the performance impact Python versions 3.8 to 3.15. I found there was variation, but generally this was a significant speed up, and that Python is trending faster on both main and this PR.

@henryiii
Copy link
Contributor

uv run asv continuous main pull/1082/head --sort default
Change Before [77a4f3f] After [7e2a5b4] <pull/1082/head> Ratio Benchmark (Parameter)
- 3.63±0.04ms 2.88±0.02ms 0.79 specifiers.TimeSpecSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.10]
- 2.99±0.06ms 2.39±0.05ms 0.8 specifiers.TimeSpecSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.12]
- 2.78±0.01ms 2.25±0.03ms 0.81 specifiers.TimeSpecSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.13]
- 2.85±0.06ms 2.37±0.1ms 0.83 specifiers.TimeSpecSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.14]
- 3.59±0.03ms 2.85±0.01ms 0.79 specifiers.TimeSpecSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.8]
- 3.65±0.1ms 2.84±0.05ms 0.78 specifiers.TimeSpecSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.9]
- 4.09±0.01ms 3.21±0.01ms 0.78 specifiers.TimeSpecSuite.time_contains [PU-H2WF61JRQ6NY/virtualenv-py3.10]
- 3.02±0.03ms 2.43±0.02ms 0.8 specifiers.TimeSpecSuite.time_contains [PU-H2WF61JRQ6NY/virtualenv-py3.11]
- 3.17±0.07ms 2.54±0.07ms 0.8 specifiers.TimeSpecSuite.time_contains [PU-H2WF61JRQ6NY/virtualenv-py3.12]
- 2.81±0.05ms 2.27±0.02ms 0.81 specifiers.TimeSpecSuite.time_contains [PU-H2WF61JRQ6NY/virtualenv-py3.13]
- 2.92±0.02ms 2.27±0.02ms 0.78 specifiers.TimeSpecSuite.time_contains [PU-H2WF61JRQ6NY/virtualenv-py3.14]
- 4.04±0.05ms 3.20±0.03ms 0.79 specifiers.TimeSpecSuite.time_contains [PU-H2WF61JRQ6NY/virtualenv-py3.8]
- 4.07±0.1ms 3.29±0.02ms 0.81 specifiers.TimeSpecSuite.time_contains [PU-H2WF61JRQ6NY/virtualenv-py3.9]
- 2.21±0.09ms 1.31±0ms 0.59 version.TimeVersionSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.10]
- 1.44±0.01ms 871±10μs 0.6 version.TimeVersionSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.11]
- 1.74±0ms 968±7μs 0.56 version.TimeVersionSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.12]
- 1.61±0ms 878±4μs 0.55 version.TimeVersionSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.13]
- 1.64±0.01ms 857±10μs 0.52 version.TimeVersionSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.14]
- 2.17±0.03ms 1.29±0ms 0.6 version.TimeVersionSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.8]
- 2.07±0.01ms 1.27±0.01ms 0.62 version.TimeVersionSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.9]
- 3.09±0.05ms 2.21±0.03ms 0.71 version.TimeVersionSuite.time_hash [PU-H2WF61JRQ6NY/virtualenv-py3.10]
- 2.12±0.01ms 1.51±0.01ms 0.71 version.TimeVersionSuite.time_hash [PU-H2WF61JRQ6NY/virtualenv-py3.11]
- 2.55±0ms 1.81±0.01ms 0.71 version.TimeVersionSuite.time_hash [PU-H2WF61JRQ6NY/virtualenv-py3.12]
- 2.30±0.01ms 1.66±0.2ms 0.72 version.TimeVersionSuite.time_hash [PU-H2WF61JRQ6NY/virtualenv-py3.13]
- 2.37±0ms 1.56±0.01ms 0.66 version.TimeVersionSuite.time_hash [PU-H2WF61JRQ6NY/virtualenv-py3.14]
- 3.06±0.1ms 2.12±0.01ms 0.69 version.TimeVersionSuite.time_hash [PU-H2WF61JRQ6NY/virtualenv-py3.8]
- 2.99±0.02ms 2.16±0.01ms 0.72 version.TimeVersionSuite.time_hash [PU-H2WF61JRQ6NY/virtualenv-py3.9]
- 4.59±0.08ms 3.68±0ms 0.8 version.TimeVersionSuite.time_sort [PU-H2WF61JRQ6NY/virtualenv-py3.10]
- 3.44±0ms 2.89±0.01ms 0.84 version.TimeVersionSuite.time_sort [PU-H2WF61JRQ6NY/virtualenv-py3.11]
- 3.44±0.02ms 2.69±0.02ms 0.78 version.TimeVersionSuite.time_sort [PU-H2WF61JRQ6NY/virtualenv-py3.12]
- 3.14±0.04ms 2.37±0.01ms 0.75 version.TimeVersionSuite.time_sort [PU-H2WF61JRQ6NY/virtualenv-py3.13]
- 3.30±0.2ms 2.34±0.03ms 0.71 version.TimeVersionSuite.time_sort [PU-H2WF61JRQ6NY/virtualenv-py3.14]
- 4.72±0.02ms 3.89±0ms 0.82 version.TimeVersionSuite.time_sort [PU-H2WF61JRQ6NY/virtualenv-py3.8]
- 4.77±0.1ms 3.86±0.01ms 0.81 version.TimeVersionSuite.time_sort [PU-H2WF61JRQ6NY/virtualenv-py3.9]
- 2.88±0.02ms 1.98±0ms 0.69 version.TimeVersionSuite.time_str [PU-H2WF61JRQ6NY/virtualenv-py3.10]
- 2.33±0.01ms 1.55±0.2ms 0.67 version.TimeVersionSuite.time_str [PU-H2WF61JRQ6NY/virtualenv-py3.12]
- 2.10±0.04ms 1.36±0.01ms 0.65 version.TimeVersionSuite.time_str [PU-H2WF61JRQ6NY/virtualenv-py3.13]
- 2.22±0.02ms 1.40±0.01ms 0.63 version.TimeVersionSuite.time_str [PU-H2WF61JRQ6NY/virtualenv-py3.14]
- 2.91±0.01ms 2.04±0.02ms 0.7 version.TimeVersionSuite.time_str [PU-H2WF61JRQ6NY/virtualenv-py3.8]

SOME BENCHMARKS HAVE CHANGED SIGNIFICANTLY.
PERFORMANCE INCREASED.

@henryiii
Copy link
Contributor

henryiii commented Feb 11, 2026

FYI, I tried always stripping, and dropping the additional whitespace from the regex:

diff --git a/src/packaging/version.py b/src/packaging/version.py
index 95c8e8d..f159323 100644
--- a/src/packaging/version.py
+++ b/src/packaging/version.py
@@ -343,7 +343,7 @@ class Version(_BaseVersion):
     __slots__ = ("_dev", "_epoch", "_key_cache", "_local", "_post", "_pre", "_release")
     __match_args__ = ("_str",)

-    _regex = re.compile(r"\s*" + VERSION_PATTERN + r"\s*", re.VERBOSE | re.IGNORECASE)
+    _regex = re.compile(VERSION_PATTERN, re.VERBOSE | re.IGNORECASE)

     _epoch: int
     _release: tuple[int, ...]
@@ -364,6 +364,7 @@ class Version(_BaseVersion):
             If the ``version`` does not conform to PEP 440 in any way then this
             exception will be raised.
         """
+        version = version.strip()
         if _SIMPLE_VERSION_INDICATORS.issuperset(version):
             try:
                 self._release = tuple(map(int, version.split(".")))
$ uv run asv continuous add-fast-path-for-parsing-simple-versions^ add-fast-path-for-parsing-simple-versions --sort default --no-only-changed
...
BENCHMARKS NOT SIGNIFICANTLY CHANGED.

Looking at the results, it seems to me it is a hair (1-2%) slower on average. By the way, I had to do that with #1059, the separate repo would have requirement me to push it somewhere with an open PR.

Table
Change Before [7e2a5b4] <add-fast-path-for-parsing-simple-versions~1> After [13c6c7a2] Ratio Benchmark (Parameter)
2.49±0.01ms 2.48±0.01ms 1 markers.TimeMarkerSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.10]
1.75±0ms 1.78±0ms 1.01 markers.TimeMarkerSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.11]
2.03±0ms 2.03±0ms 1 markers.TimeMarkerSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.12]
1.82±0.01ms 1.79±0.01ms 0.98 markers.TimeMarkerSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.13]
2.28±0ms 2.29±0.01ms 1 markers.TimeMarkerSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.14]
2.80±0ms 2.78±0.02ms 1 markers.TimeMarkerSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.8]
2.59±0.02ms 2.67±0.03ms 1.03 markers.TimeMarkerSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.9]
815±7μs 816±2μs 1 markers.TimeMarkerSuite.time_evaluate [PU-H2WF61JRQ6NY/virtualenv-py3.10]
555±3μs 558±2μs 1 markers.TimeMarkerSuite.time_evaluate [PU-H2WF61JRQ6NY/virtualenv-py3.11]
605±5μs 607±5μs 1 markers.TimeMarkerSuite.time_evaluate [PU-H2WF61JRQ6NY/virtualenv-py3.12]
535±3μs 534±4μs 1 markers.TimeMarkerSuite.time_evaluate [PU-H2WF61JRQ6NY/virtualenv-py3.13]
731±2μs 732±1μs 1 markers.TimeMarkerSuite.time_evaluate [PU-H2WF61JRQ6NY/virtualenv-py3.14]
783±5μs 779±1μs 0.99 markers.TimeMarkerSuite.time_evaluate [PU-H2WF61JRQ6NY/virtualenv-py3.8]
795±10μs 796±10μs 1 markers.TimeMarkerSuite.time_evaluate [PU-H2WF61JRQ6NY/virtualenv-py3.9]
10.1±0.07ms 10.0±0.1ms 0.99 requirement.TimeRequirementSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.10]
7.43±0.09ms 7.47±0.05ms 1.01 requirement.TimeRequirementSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.11]
8.05±0.06ms 8.12±0.02ms 1.01 requirement.TimeRequirementSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.12]
7.16±0.02ms 7.10±0.04ms 0.99 requirement.TimeRequirementSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.13]
8.83±0.02ms 8.78±0.03ms 0.99 requirement.TimeRequirementSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.14]
10.6±0.01ms 10.6±0.1ms 1 requirement.TimeRequirementSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.8]
10.3±0.01ms 10.3±0.05ms 1 requirement.TimeRequirementSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.9]
569±7μs 570±2μs 1 resolver.TimeResolverSuite.time_resolver_loop [PU-H2WF61JRQ6NY/virtualenv-py3.10]
425±3μs 424±5μs 1 resolver.TimeResolverSuite.time_resolver_loop [PU-H2WF61JRQ6NY/virtualenv-py3.11]
418±5μs 420±7μs 1 resolver.TimeResolverSuite.time_resolver_loop [PU-H2WF61JRQ6NY/virtualenv-py3.12]
387±4μs 389±0.9μs 1 resolver.TimeResolverSuite.time_resolver_loop [PU-H2WF61JRQ6NY/virtualenv-py3.13]
507±5μs 519±8μs 1.02 resolver.TimeResolverSuite.time_resolver_loop [PU-H2WF61JRQ6NY/virtualenv-py3.14]
554±5μs 569±3μs 1.03 resolver.TimeResolverSuite.time_resolver_loop [PU-H2WF61JRQ6NY/virtualenv-py3.8]
580±3μs 574±4μs 0.99 resolver.TimeResolverSuite.time_resolver_loop [PU-H2WF61JRQ6NY/virtualenv-py3.9]
2.87±0.01ms 2.86±0.02ms 1 specifiers.TimeSpecSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.10]
2.21±0.01ms 2.21±0.01ms 1 specifiers.TimeSpecSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.11]
2.38±0.03ms 2.36±0.01ms 0.99 specifiers.TimeSpecSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.12]
2.21±0.02ms 2.20±0.02ms 1 specifiers.TimeSpecSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.13]
2.62±0.04ms 2.63±0.02ms 1 specifiers.TimeSpecSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.14]
2.90±0.04ms 2.85±0.01ms 0.98 specifiers.TimeSpecSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.8]
2.79±0.01ms 2.80±0.02ms 1 specifiers.TimeSpecSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.9]
3.24±0.04ms 3.31±0.05ms 1.02 specifiers.TimeSpecSuite.time_contains [PU-H2WF61JRQ6NY/virtualenv-py3.10]
2.42±0.01ms 2.42±0.02ms 1 specifiers.TimeSpecSuite.time_contains [PU-H2WF61JRQ6NY/virtualenv-py3.11]
2.57±0.1ms 2.51±0.01ms 0.98 specifiers.TimeSpecSuite.time_contains [PU-H2WF61JRQ6NY/virtualenv-py3.12]
2.27±0.01ms 2.27±0.03ms 1 specifiers.TimeSpecSuite.time_contains [PU-H2WF61JRQ6NY/virtualenv-py3.13]
2.94±0.01ms 3.00±0.01ms 1.02 specifiers.TimeSpecSuite.time_contains [PU-H2WF61JRQ6NY/virtualenv-py3.14]
3.26±0.03ms 3.28±0.01ms 1.01 specifiers.TimeSpecSuite.time_contains [PU-H2WF61JRQ6NY/virtualenv-py3.8]
3.27±0.07ms 3.34±0.04ms 1.02 specifiers.TimeSpecSuite.time_contains [PU-H2WF61JRQ6NY/virtualenv-py3.9]
82.3±1μs 81.6±0.3μs 0.99 specifiers.TimeSpecSuite.time_filter [PU-H2WF61JRQ6NY/virtualenv-py3.10]
59.0±0.06μs 59.4±0.4μs 1.01 specifiers.TimeSpecSuite.time_filter [PU-H2WF61JRQ6NY/virtualenv-py3.11]
46.7±0.09μs 46.8±0.1μs 1 specifiers.TimeSpecSuite.time_filter [PU-H2WF61JRQ6NY/virtualenv-py3.12]
42.4±0.4μs 41.9±0.07μs 0.99 specifiers.TimeSpecSuite.time_filter [PU-H2WF61JRQ6NY/virtualenv-py3.13]
62.0±0.1μs 62.4±0.3μs 1.01 specifiers.TimeSpecSuite.time_filter [PU-H2WF61JRQ6NY/virtualenv-py3.14]
81.9±0.3μs 81.6±0.03μs 1 specifiers.TimeSpecSuite.time_filter [PU-H2WF61JRQ6NY/virtualenv-py3.8]
83.2±0.2μs 83.7±0.1μs 1.01 specifiers.TimeSpecSuite.time_filter [PU-H2WF61JRQ6NY/virtualenv-py3.9]
3.08±0.03μs 3.04±0μs 0.99 utils.TimeUtils.time_canonicalize_name [PU-H2WF61JRQ6NY/virtualenv-py3.10]
2.32±0.01μs 2.31±0μs 1 utils.TimeUtils.time_canonicalize_name [PU-H2WF61JRQ6NY/virtualenv-py3.11]
2.40±0.02μs 2.37±0.01μs 0.99 utils.TimeUtils.time_canonicalize_name [PU-H2WF61JRQ6NY/virtualenv-py3.12]
2.39±0μs 2.39±0μs 1 utils.TimeUtils.time_canonicalize_name [PU-H2WF61JRQ6NY/virtualenv-py3.13]
3.20±0.02μs 3.11±0.03μs 0.97 utils.TimeUtils.time_canonicalize_name [PU-H2WF61JRQ6NY/virtualenv-py3.14]
3.11±0μs 3.13±0.02μs 1.01 utils.TimeUtils.time_canonicalize_name [PU-H2WF61JRQ6NY/virtualenv-py3.8]
3.11±0.01μs 3.11±0μs 1 utils.TimeUtils.time_canonicalize_name [PU-H2WF61JRQ6NY/virtualenv-py3.9]
1.31±0.01ms 1.32±0.02ms 1.01 version.TimeVersionSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.10]
808±6μs 817±2μs 1.01 version.TimeVersionSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.11]
959±4μs 966±2μs 1.01 version.TimeVersionSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.12]
887±2μs 875±1μs 0.99 version.TimeVersionSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.13]
994±2μs 1.01±0ms 1.02 version.TimeVersionSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.14]
1.28±0.01ms 1.30±0.01ms 1.02 version.TimeVersionSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.8]
1.28±0.02ms 1.29±0.02ms 1.01 version.TimeVersionSuite.time_constructor [PU-H2WF61JRQ6NY/virtualenv-py3.9]
2.15±0.01ms 2.18±0.01ms 1.01 version.TimeVersionSuite.time_hash [PU-H2WF61JRQ6NY/virtualenv-py3.10]
1.47±0ms 1.49±0ms 1.01 version.TimeVersionSuite.time_hash [PU-H2WF61JRQ6NY/virtualenv-py3.11]
1.79±0.01ms 1.77±0.01ms 0.99 version.TimeVersionSuite.time_hash [PU-H2WF61JRQ6NY/virtualenv-py3.12]
1.54±0ms 1.56±0ms 1.01 version.TimeVersionSuite.time_hash [PU-H2WF61JRQ6NY/virtualenv-py3.13]
1.93±0.01ms 1.93±0ms 1 version.TimeVersionSuite.time_hash [PU-H2WF61JRQ6NY/virtualenv-py3.14]
2.12±0ms 2.14±0ms 1.01 version.TimeVersionSuite.time_hash [PU-H2WF61JRQ6NY/virtualenv-py3.8]
2.12±0.02ms 2.16±0.01ms 1.02 version.TimeVersionSuite.time_hash [PU-H2WF61JRQ6NY/virtualenv-py3.9]
3.63±0ms 3.68±0ms 1.01 version.TimeVersionSuite.time_sort [PU-H2WF61JRQ6NY/virtualenv-py3.10]
2.78±0.01ms 2.88±0.02ms 1.03 version.TimeVersionSuite.time_sort [PU-H2WF61JRQ6NY/virtualenv-py3.11]
2.64±0.01ms 2.68±0.02ms 1.02 version.TimeVersionSuite.time_sort [PU-H2WF61JRQ6NY/virtualenv-py3.12]
2.36±0ms 2.37±0.01ms 1 version.TimeVersionSuite.time_sort [PU-H2WF61JRQ6NY/virtualenv-py3.13]
3.14±0.01ms 3.14±0ms 1 version.TimeVersionSuite.time_sort [PU-H2WF61JRQ6NY/virtualenv-py3.14]
3.91±0.01ms 3.91±0.01ms 1 version.TimeVersionSuite.time_sort [PU-H2WF61JRQ6NY/virtualenv-py3.8]
3.87±0.01ms 3.90±0.03ms 1.01 version.TimeVersionSuite.time_sort [PU-H2WF61JRQ6NY/virtualenv-py3.9]
1.99±0ms 2.01±0.01ms 1.01 version.TimeVersionSuite.time_str [PU-H2WF61JRQ6NY/virtualenv-py3.10]
1.41±0.01ms 1.45±0.01ms 1.03 version.TimeVersionSuite.time_str [PU-H2WF61JRQ6NY/virtualenv-py3.11]
1.54±0.01ms 1.55±0ms 1.01 version.TimeVersionSuite.time_str [PU-H2WF61JRQ6NY/virtualenv-py3.12]
1.34±0.01ms 1.38±0.01ms 1.03 version.TimeVersionSuite.time_str [PU-H2WF61JRQ6NY/virtualenv-py3.13]
1.66±0ms 1.69±0.03ms 1.01 version.TimeVersionSuite.time_str [PU-H2WF61JRQ6NY/virtualenv-py3.14]
2.06±0.01ms 2.05±0.01ms 1 version.TimeVersionSuite.time_str [PU-H2WF61JRQ6NY/virtualenv-py3.8]
2.00±0ms 2.04±0.02ms 1.02 version.TimeVersionSuite.time_str [PU-H2WF61JRQ6NY/virtualenv-py3.9]

@henryiii henryiii force-pushed the add-fast-path-for-parsing-simple-versions branch from 984e2de to d6c0a36 Compare February 11, 2026 23:14
@henryiii henryiii merged commit ee13ba2 into pypa:main Feb 11, 2026
44 checks passed
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.

3 participants