Skip to content

perf: Speed up complex SpecifierSet filtering by implementing cost based ordering - #1105

Merged
notatallshaw merged 11 commits into
pypa:mainfrom
notatallshaw:specifier-cost-based-ordering
Mar 8, 2026
Merged

notatallshaw merged 11 commits into
pypa:mainfrom
notatallshaw:specifier-cost-based-ordering

Conversation

@notatallshaw

@notatallshaw notatallshaw commented Mar 7, 2026 •

Copy link
Copy Markdown
Member

This flattens out the execution of individual Specifiers in SpecifierSet.filter so that it can re-order them based on cost and likelihood to reject, as some operators are more than 5x expensive to call than others.

For example the filtered_versions is calculated about 3x faster:

specifier = SpecifierSet("!=1.*,<1.9")
versions = [Version(str(i / 10)) for i in range(1, 101)]
filtered_versions = list(specifier.filter(versions))

And despite the more complex code the performance of simple specifiers seems not to be measurably impacted.

@notatallshaw
notatallshaw force-pushed the specifier-cost-based-ordering branch from 1429ec5 to e516a49 Compare March 7, 2026 04:50
@notatallshaw notatallshaw changed the title perf: Speed up complex specifiers by ordering specifiers by cost in SpecifierSet.filter perf: Speed up complex SpecifierSet filtering by implementing cost based ordering Mar 7, 2026
@notatallshaw
notatallshaw force-pushed the specifier-cost-based-ordering branch 7 times, most recently from 9fb9784 to 0bebcfb Compare March 7, 2026 05:34
@henryiii
henryiii force-pushed the specifier-cost-based-ordering branch from 0bebcfb to 6acd57e Compare March 7, 2026 07:10
@henryiii

henryiii commented Mar 7, 2026 •

Copy link
Copy Markdown
Contributor

https://github.com/pypa/packaging/actions/runs/22794469746

About 50% slower? Unless I got the order backward.

@notatallshaw

notatallshaw commented Mar 7, 2026 •

Copy link
Copy Markdown
Member Author

About 50% slower? Unless I got the order backward.

That's benchmark variance and I've found the cause of why SpecifierSet is so noisy, I'll have a PR shortly that fixes benchmark noise.

Once variance is fixed a real regression is revealed in the resolve loop benchmarks, I will come back to this PR after we land that.

@notatallshaw
notatallshaw marked this pull request as draft March 7, 2026 15:13
@henryiii
henryiii force-pushed the specifier-cost-based-ordering branch from 6acd57e to f8c34cd Compare March 7, 2026 18:19
@henryiii

henryiii commented Mar 7, 2026 •

Copy link
Copy Markdown
Contributor

Same results, just with a little less variance perhaps. The fact the mini resolver gets 15% slower worries me the most.

All benchmarks:

Change Before [20f1523] After [47f8bed] Ratio Benchmark (Parameter)
+ 1.28±0.02ms 1.43±0.02ms 1.12 resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]
+ 891±20μs 1.02±0.01ms 1.15 resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]
+ 892±7μs 1.11±0.01ms 1.24 resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]
+ 877±10μs 1.02±0.01ms 1.16 resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]
+ 867±20μs 1.02±0.01ms 1.18 resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]
+ 1.32±0ms 1.47±0.02ms 1.12 resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]
+ 1.30±0ms 1.43±0.02ms 1.1 resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]
8.76±0.05ms 9.49±0.06ms 1.08 specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]
6.15±0.06ms 6.74±0.1ms 1.1 specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]
+ 6.74±0.05ms 7.72±0.1ms 1.14 specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]
6.59±0.05ms 7.07±0.05ms 1.07 specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]
+ 6.37±0.1ms 7.07±0.1ms 1.11 specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]
9.23±0.1ms 9.76±0.08ms 1.06 specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]
9.05±0.08ms 9.69±0.06ms 1.07 specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]
6.45±0.06ms 7.09±0.1ms 1.1 specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]
+ 4.77±0.05ms 5.26±0.04ms 1.1 specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]
+ 5.10±0.05ms 6.01±0.1ms 1.18 specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]
+ 4.89±0.04ms 5.42±0.04ms 1.11 specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]
+ 4.90±0.08ms 5.42±0.03ms 1.11 specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]
6.74±0.05ms 7.35±0.08ms 1.09 specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]
6.59±0.05ms 7.12±0.05ms 1.08 specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]
+ 18.2±0.2μs 22.0±0.3μs 1.21 specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]
+ 12.0±0.06μs 15.4±0.1μs 1.29 specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]
+ 10.4±0.09μs 16.9±0.1μs 1.62 specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]
+ 10.1±0.1μs 14.5±0.1μs 1.44 specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]
+ 9.72±0.1μs 13.9±0.04μs 1.43 specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]
+ 19.0±0.08μs 23.1±0.2μs 1.22 specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]
+ 19.0±0.2μs 22.7±0.1μs 1.19 specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]
+ 15.6±0.09μs 18.9±0.3μs 1.21 specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]
+ 10.3±0.09μs 13.6±0.1μs 1.32 specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]
+ 8.37±0.05μs 14.7±0.1μs 1.76 specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]
+ 8.48±0.1μs 12.7±0.1μs 1.49 specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]
+ 8.23±0.07μs 12.2±0.05μs 1.49 specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]
+ 16.3±0.2μs 20.1±0.2μs 1.23 specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]
+ 16.3±0.3μs 19.5±0.09μs 1.2 specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]

SOME BENCHMARKS HAVE CHANGED SIGNIFICANTLY.
PERFORMANCE DECREASED.

@notatallshaw

Copy link
Copy Markdown
Member Author

FYI, I have fixes for the regressions in the benchmarks, but first I'm going to look at self._specs, which I think is the wrong data structure for what it's doing.

notatallshaw and others added 9 commits March 7, 2026 21:26
Skip CBO sorting overhead when there is only one specifier
by delegating directly to Specifier.filter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Avoid re-sorting specifier checks on every _filter_specs call
by caching the resolved and sorted checks list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use hasattr guard for compatibility with main which lacks
this attribute.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add filter and contains benchmarks using a multi-operator
specifier set to measure CBO effectiveness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@notatallshaw
notatallshaw force-pushed the specifier-cost-based-ordering branch from f8c34cd to 7c645a4 Compare March 8, 2026 02:57
@notatallshaw

Copy link
Copy Markdown
Member Author

Fixed regressions, added a new "complex" specifier set of benchmarks which are designed to show off the improvement, the resolver loop seems a little faster, possibly contains cold simple is a hair slower (but could be noise or could be the hasattr branches which can be removed some time after this lands):

All benchmarks:

| Change   | Before [9b972011]    | After [2a79383f]    |   Ratio | Benchmark (Parameter)                                                                                 |
|----------|----------------------|---------------------|---------|-------------------------------------------------------------------------------------------------------|
|          | 5.64±0.04ms          | 5.71±0.06ms         |    1.01 | markers.TimeMarkerSuite.time_constructor [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]            |
|          | 4.03±0.04ms          | 3.99±0.04ms         |    0.99 | markers.TimeMarkerSuite.time_constructor [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]            |
|          | 4.49±0.04ms          | 4.57±0.08ms         |    1.02 | markers.TimeMarkerSuite.time_constructor [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]            |
|          | 4.62±0.03ms          | 4.60±0.08ms         |    0.99 | markers.TimeMarkerSuite.time_constructor [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]            |
|          | 4.45±0.04ms          | 4.42±0.04ms         |    0.99 | markers.TimeMarkerSuite.time_constructor [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]            |
|          | 5.84±0.04ms          | 5.88±0.08ms         |    1.01 | markers.TimeMarkerSuite.time_constructor [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]             |
|          | 5.97±0.06ms          | 5.95±0.06ms         |    1    | markers.TimeMarkerSuite.time_constructor [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]             |
|          | 1.81±0.02ms          | 1.86±0.03ms         |    1.03 | markers.TimeMarkerSuite.time_evaluate [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]               |
|          | 1.20±0.02ms          | 1.20±0.01ms         |    1    | markers.TimeMarkerSuite.time_evaluate [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]               |
|          | 1.35±0.02ms          | 1.35±0.01ms         |    1    | markers.TimeMarkerSuite.time_evaluate [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]               |
|          | 1.34±0.04ms          | 1.33±0.01ms         |    0.99 | markers.TimeMarkerSuite.time_evaluate [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]               |
|          | 1.33±0.01ms          | 1.33±0.01ms         |    1    | markers.TimeMarkerSuite.time_evaluate [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]               |
|          | 1.91±0.01ms          | 1.89±0.03ms         |    0.99 | markers.TimeMarkerSuite.time_evaluate [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]                |
|          | 1.80±0.01ms          | 1.80±0.02ms         |    1    | markers.TimeMarkerSuite.time_evaluate [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]                |
|          | 20.0±0.2ms           | 20.2±0.2ms          |    1.01 | requirement.TimeRequirementSuite.time_constructor [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]   |
|          | 14.8±0.3ms           | 14.5±0.2ms          |    0.98 | requirement.TimeRequirementSuite.time_constructor [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]   |
|          | 15.7±0.09ms          | 15.7±0.06ms         |    1    | requirement.TimeRequirementSuite.time_constructor [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]   |
|          | 17.0±0.2ms           | 16.6±0.4ms          |    0.98 | requirement.TimeRequirementSuite.time_constructor [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]   |
|          | 15.9±0.3ms           | 15.9±0.1ms          |    1    | requirement.TimeRequirementSuite.time_constructor [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]   |
|          | 21.2±0.2ms           | 21.5±0.5ms          |    1.01 | requirement.TimeRequirementSuite.time_constructor [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]    |
|          | 21.1±0.09ms          | 21.3±0.2ms          |    1.01 | requirement.TimeRequirementSuite.time_constructor [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]    |
|          | 1.14±0.01ms          | 1.04±0.01ms         |    0.91 | resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]       |
|          | 786±9μs              | 721±5μs             |    0.92 | resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]       |
|          | 814±7μs              | 775±9μs             |    0.95 | resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]       |
| -        | 842±10μs             | 764±20μs            |    0.91 | resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]       |
|          | 772±3μs              | 726±5μs             |    0.94 | resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]       |
|          | 1.21±0.01ms          | 1.11±0ms            |    0.91 | resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]        |
| -        | 1.19±0.01ms          | 1.08±0.01ms         |    0.91 | resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]        |
|          | 2.14±0.01ms          | 2.17±0.04ms         |    1.01 | specifiers.TimeSpecSuite.time_constructor [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]           |
|          | 1.85±0.02ms          | 1.87±0.02ms         |    1.01 | specifiers.TimeSpecSuite.time_constructor [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]           |
|          | 2.09±0.03ms          | 2.11±0.02ms         |    1.01 | specifiers.TimeSpecSuite.time_constructor [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]           |
|          | 2.00±0.03ms          | 2.05±0.04ms         |    1.03 | specifiers.TimeSpecSuite.time_constructor [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]           |
|          | 1.98±0.01ms          | 2.00±0.03ms         |    1.01 | specifiers.TimeSpecSuite.time_constructor [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]           |
|          | 2.18±0.02ms          | 2.19±0.02ms         |    1.01 | specifiers.TimeSpecSuite.time_constructor [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]            |
|          | 2.12±0.02ms          | 2.13±0.04ms         |    1.01 | specifiers.TimeSpecSuite.time_constructor [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]            |
|          | 8.91±0.1ms           | 9.29±0.1ms          |    1.04 | specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]         |
|          | 6.06±0.04ms          | 6.36±0.05ms         |    1.05 | specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]         |
|          | 6.59±0.08ms          | 7.01±0.06ms         |    1.06 | specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]         |
|          | 7.11±0.1ms           | 7.17±0.4ms          |    1.01 | specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]         |
|          | 6.30±0.04ms          | 6.78±0.1ms          |    1.08 | specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]         |
|          | 9.37±0.07ms          | 9.59±0.08ms         |    1.02 | specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]          |
|          | 9.35±0.2ms           | 9.50±0.07ms         |    1.02 | specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]          |
| -        | 4.93±0.04ms          | 2.63±0.02ms         |    0.53 | specifiers.TimeSpecSuite.time_contains_complex_warm [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0] |
| -        | 3.68±0.08ms          | 1.93±0.01ms         |    0.52 | specifiers.TimeSpecSuite.time_contains_complex_warm [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0] |
| -        | 3.95±0.06ms          | 2.14±0.02ms         |    0.54 | specifiers.TimeSpecSuite.time_contains_complex_warm [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0] |
| -        | 4.23±0.06ms          | 2.18±0.01ms         |    0.52 | specifiers.TimeSpecSuite.time_contains_complex_warm [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0] |
| -        | 3.71±0.06ms          | 1.97±0.02ms         |    0.53 | specifiers.TimeSpecSuite.time_contains_complex_warm [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0] |
| -        | 5.23±0.04ms          | 2.82±0.03ms         |    0.54 | specifiers.TimeSpecSuite.time_contains_complex_warm [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]  |
| -        | 5.11±0.04ms          | 2.73±0.01ms         |    0.54 | specifiers.TimeSpecSuite.time_contains_complex_warm [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]  |
|          | 6.45±0.04ms          | 6.32±0.1ms          |    0.98 | specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]         |
|          | 4.73±0.04ms          | 4.39±0.04ms         |    0.93 | specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]         |
|          | 4.99±0.1ms           | 4.78±0.05ms         |    0.96 | specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]         |
|          | 5.22±0.06ms          | 4.85±0.1ms          |    0.93 | specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]         |
|          | 4.83±0.02ms          | 4.66±0.06ms         |    0.97 | specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]         |
|          | 6.96±0.06ms          | 6.66±0.04ms         |    0.96 | specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]          |
|          | 6.79±0.02ms          | 6.46±0.03ms         |    0.95 | specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]          |
| -        | 3.54±0.02ms          | 2.04±0.03ms         |    0.57 | specifiers.TimeSpecSuite.time_filter_complex_cold [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]   |
| -        | 2.61±0.04ms          | 1.50±0.01ms         |    0.57 | specifiers.TimeSpecSuite.time_filter_complex_cold [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]   |
| -        | 2.94±0.03ms          | 1.74±0.01ms         |    0.59 | specifiers.TimeSpecSuite.time_filter_complex_cold [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]   |
| -        | 3.06±0.04ms          | 1.70±0.02ms         |    0.56 | specifiers.TimeSpecSuite.time_filter_complex_cold [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]   |
| -        | 2.69±0.03ms          | 1.55±0.01ms         |    0.58 | specifiers.TimeSpecSuite.time_filter_complex_cold [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]   |
| -        | 3.83±0.03ms          | 2.16±0.02ms         |    0.56 | specifiers.TimeSpecSuite.time_filter_complex_cold [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]    |
| -        | 3.69±0.05ms          | 2.09±0.01ms         |    0.57 | specifiers.TimeSpecSuite.time_filter_complex_cold [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]    |
| -        | 3.54±0.02ms          | 1.96±0.03ms         |    0.55 | specifiers.TimeSpecSuite.time_filter_complex_warm [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]   |
| -        | 2.62±0.04ms          | 1.49±0.02ms         |    0.57 | specifiers.TimeSpecSuite.time_filter_complex_warm [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]   |
| -        | 2.93±0.04ms          | 1.70±0.01ms         |    0.58 | specifiers.TimeSpecSuite.time_filter_complex_warm [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]   |
| -        | 2.99±0.03ms          | 1.65±0.04ms         |    0.55 | specifiers.TimeSpecSuite.time_filter_complex_warm [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]   |
| -        | 2.69±0.03ms          | 1.53±0.01ms         |    0.57 | specifiers.TimeSpecSuite.time_filter_complex_warm [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]   |
| -        | 3.80±0.07ms          | 2.12±0.02ms         |    0.56 | specifiers.TimeSpecSuite.time_filter_complex_warm [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]    |
| -        | 3.64±0.02ms          | 2.05±0.02ms         |    0.56 | specifiers.TimeSpecSuite.time_filter_complex_warm [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]    |
|          | 17.7±0.1μs           | 18.0±0.2μs          |    1.02 | specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]    |
|          | 11.9±0.2μs           | 12.0±0.1μs          |    1    | specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]    |
|          | 10.5±0.08μs          | 10.5±0.06μs         |    0.99 | specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]    |
|          | 10.3±0.2μs           | 10.2±0.1μs          |    0.99 | specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]    |
|          | 10.1±0.07μs          | 10.0±0.04μs         |    1    | specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]    |
|          | 19.5±0.2μs           | 19.4±0.06μs         |    0.99 | specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]     |
|          | 19.3±0.2μs           | 19.4±0.3μs          |    1.01 | specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]     |
|          | 15.2±0.1μs           | 15.2±0.2μs          |    1    | specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]    |
|          | 10.2±0.04μs          | 10.2±0.1μs          |    1    | specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]    |
|          | 8.50±0.07μs          | 8.38±0.02μs         |    0.99 | specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]    |
|          | 8.34±0.09μs          | 8.41±0.09μs         |    1.01 | specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]    |
|          | 8.22±0.06μs          | 8.26±0.09μs         |    1    | specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]    |
|          | 16.5±0.04μs          | 16.5±0.07μs         |    1    | specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]     |
|          | 16.4±0.09μs          | 16.4±0.2μs          |    1    | specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]     |
|          | 5.00±0.02μs          | 5.01±0.02μs         |    1    | utils.TimeUtils.time_canonicalize_name [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]              |
|          | 3.46±0.02μs          | 3.51±0.05μs         |    1.02 | utils.TimeUtils.time_canonicalize_name [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]              |
|          | 3.61±0.02μs          | 3.62±0.02μs         |    1    | utils.TimeUtils.time_canonicalize_name [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]              |
|          | 3.71±0.05μs          | 3.75±0.06μs         |    1.01 | utils.TimeUtils.time_canonicalize_name [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]              |
|          | 4.09±0.01μs          | 4.09±0.01μs         |    1    | utils.TimeUtils.time_canonicalize_name [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]              |
|          | 5.56±0.1μs           | 5.54±0.06μs         |    1    | utils.TimeUtils.time_canonicalize_name [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]               |
|          | 5.44±0.03μs          | 5.41±0.02μs         |    0.99 | utils.TimeUtils.time_canonicalize_name [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]               |
|          | 2.65±0.01ms          | 2.68±0.02ms         |    1.01 | version.TimeVersionSuite.time_constructor [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]           |
|          | 1.72±0.01ms          | 1.72±0.01ms         |    1    | version.TimeVersionSuite.time_constructor [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]           |
|          | 2.17±0.02ms          | 2.16±0.01ms         |    1    | version.TimeVersionSuite.time_constructor [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]           |
|          | 2.06±0.03ms          | 2.04±0.02ms         |    0.99 | version.TimeVersionSuite.time_constructor [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]           |
|          | 1.73±0.02ms          | 1.74±0.02ms         |    1    | version.TimeVersionSuite.time_constructor [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]           |
|          | 2.82±0.02ms          | 2.84±0.01ms         |    1.01 | version.TimeVersionSuite.time_constructor [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]            |
|          | 2.80±0.02ms          | 2.77±0.01ms         |    0.99 | version.TimeVersionSuite.time_constructor [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]            |
|          | 4.41±0.01ms          | 4.54±0.04ms         |    1.03 | version.TimeVersionSuite.time_hash [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]                  |
|          | 2.97±0.03ms          | 2.96±0.01ms         |    1    | version.TimeVersionSuite.time_hash [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]                  |
|          | 3.63±0.04ms          | 3.58±0.01ms         |    0.99 | version.TimeVersionSuite.time_hash [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]                  |
|          | 3.49±0.02ms          | 3.53±0.04ms         |    1.01 | version.TimeVersionSuite.time_hash [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]                  |
|          | 3.23±0.02ms          | 3.23±0.02ms         |    1    | version.TimeVersionSuite.time_hash [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]                  |
|          | 4.71±0.03ms          | 4.72±0.03ms         |    1    | version.TimeVersionSuite.time_hash [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]                   |
|          | 4.62±0.08ms          | 4.61±0.02ms         |    1    | version.TimeVersionSuite.time_hash [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]                   |
|          | 2.77±0.02ms          | 2.77±0.06ms         |    1    | version.TimeVersionSuite.time_sort_cold [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]             |
|          | 2.02±0.01ms          | 2.02±0.03ms         |    1    | version.TimeVersionSuite.time_sort_cold [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]             |
|          | 2.28±0.01ms          | 2.28±0.01ms         |    1    | version.TimeVersionSuite.time_sort_cold [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]             |
|          | 2.38±0.01ms          | 2.42±0.02ms         |    1.02 | version.TimeVersionSuite.time_sort_cold [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]             |
|          | 2.35±0.01ms          | 2.36±0.01ms         |    1    | version.TimeVersionSuite.time_sort_cold [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]             |
|          | 3.52±0.03ms          | 3.56±0.02ms         |    1.01 | version.TimeVersionSuite.time_sort_cold [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]              |
|          | 3.32±0.04ms          | 3.32±0.03ms         |    1    | version.TimeVersionSuite.time_sort_cold [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]              |
|          | 2.20±0.02ms          | 2.21±0.02ms         |    1.01 | version.TimeVersionSuite.time_sort_warm [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]             |
|          | 1.65±0.01ms          | 1.66±0.01ms         |    1    | version.TimeVersionSuite.time_sort_warm [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]             |
|          | 1.90±0.01ms          | 1.90±0.01ms         |    1    | version.TimeVersionSuite.time_sort_warm [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]             |
|          | 2.00±0.01ms          | 2.00±0.02ms         |    1    | version.TimeVersionSuite.time_sort_warm [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]             |
|          | 1.96±0.01ms          | 1.95±0.01ms         |    1    | version.TimeVersionSuite.time_sort_warm [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]             |
|          | 2.85±0.01ms          | 2.86±0.01ms         |    1.01 | version.TimeVersionSuite.time_sort_warm [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]              |
|          | 2.59±0.04ms          | 2.56±0.01ms         |    0.99 | version.TimeVersionSuite.time_sort_warm [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]              |
|          | 4.34±0.03ms          | 4.35±0.05ms         |    1    | version.TimeVersionSuite.time_str [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]                   |
|          | 3.15±0.02ms          | 3.14±0.01ms         |    1    | version.TimeVersionSuite.time_str [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]                   |
|          | 3.54±0.02ms          | 3.54±0.02ms         |    1    | version.TimeVersionSuite.time_str [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]                   |
|          | 3.28±0.05ms          | 3.26±0.02ms         |    0.99 | version.TimeVersionSuite.time_str [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]                   |
|          | 3.09±0.06ms          | 3.10±0.04ms         |    1    | version.TimeVersionSuite.time_str [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]                   |
|          | 4.88±0.03ms          | 4.86±0.02ms         |    1    | version.TimeVersionSuite.time_str [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]                    |
|          | 4.67±0.02ms          | 4.66±0.02ms         |    1    | version.TimeVersionSuite.time_str [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]                    |

SOME BENCHMARKS HAVE CHANGED SIGNIFICANTLY.
PERFORMANCE INCREASED.

@notatallshaw
notatallshaw marked this pull request as ready for review March 8, 2026 03:09
@notatallshaw

notatallshaw commented Mar 8, 2026 •

Copy link
Copy Markdown
Member Author

This relates back to why SpecifierSet performance had such high variance, when specifiers were stored in a set they were randomly ordered when iterating.

Some combinations of specifiers were much faster because the initial specifier was cheap and/or filtered lots of versions, meaning remaining specifiers didn't need to do anything. Whereas other combinations of specifiers were much slower because the initial specifier was expensive and/or didn't filter any versions meaning versions kept having to be checked by specifiers.

This attempts to make the former situation more likely by using a heuristic on the cost and effectiveness of the operator in the specifier.

@notatallshaw

Copy link
Copy Markdown
Member Author

I slightly touched up the heuristic cost function as I realized that if == or === were anywhere in a complex specifier they should be evaluated first, as they will likely result in 1 or 0 versions, drastically reducing the work for any other specifier.

Benchmarks are within noise values of prior to this commit:

All benchmarks:

| Change   | Before [9b972011]    | After [ca02c9dd]    |   Ratio | Benchmark (Parameter)                                                                                 |
|----------|----------------------|---------------------|---------|-------------------------------------------------------------------------------------------------------|
|          | 5.67±0.08ms          | 5.83±0.03ms         |    1.03 | markers.TimeMarkerSuite.time_constructor [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]            |
|          | 4.00±0.04ms          | 4.05±0.04ms         |    1.01 | markers.TimeMarkerSuite.time_constructor [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]            |
|          | 4.55±0.05ms          | 4.52±0.02ms         |    0.99 | markers.TimeMarkerSuite.time_constructor [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]            |
|          | 4.40±0.04ms          | 4.41±0.05ms         |    1    | markers.TimeMarkerSuite.time_constructor [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]            |
|          | 4.45±0.04ms          | 4.42±0.02ms         |    0.99 | markers.TimeMarkerSuite.time_constructor [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]            |
|          | 5.88±0.08ms          | 5.91±0.06ms         |    1.01 | markers.TimeMarkerSuite.time_constructor [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]             |
|          | 5.91±0.06ms          | 6.11±0.05ms         |    1.03 | markers.TimeMarkerSuite.time_constructor [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]             |
|          | 1.85±0.01ms          | 1.82±0.03ms         |    0.98 | markers.TimeMarkerSuite.time_evaluate [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]               |
|          | 1.20±0.01ms          | 1.20±0.01ms         |    1    | markers.TimeMarkerSuite.time_evaluate [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]               |
|          | 1.38±0.01ms          | 1.38±0.02ms         |    1    | markers.TimeMarkerSuite.time_evaluate [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]               |
|          | 1.25±0.01ms          | 1.28±0.02ms         |    1.02 | markers.TimeMarkerSuite.time_evaluate [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]               |
|          | 1.29±0.01ms          | 1.31±0.02ms         |    1.01 | markers.TimeMarkerSuite.time_evaluate [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]               |
|          | 1.88±0.03ms          | 1.91±0.03ms         |    1.01 | markers.TimeMarkerSuite.time_evaluate [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]                |
|          | 1.79±0.02ms          | 1.81±0.01ms         |    1.01 | markers.TimeMarkerSuite.time_evaluate [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]                |
|          | 20.4±0.2ms           | 20.6±0.2ms          |    1.01 | requirement.TimeRequirementSuite.time_constructor [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]   |
|          | 14.3±0.1ms           | 14.5±0.3ms          |    1.02 | requirement.TimeRequirementSuite.time_constructor [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]   |
|          | 15.7±0.2ms           | 15.6±0.07ms         |    0.99 | requirement.TimeRequirementSuite.time_constructor [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]   |
|          | 15.5±0.2ms           | 15.5±0.1ms          |    1    | requirement.TimeRequirementSuite.time_constructor [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]   |
|          | 15.8±0.1ms           | 15.7±0.2ms          |    0.99 | requirement.TimeRequirementSuite.time_constructor [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]   |
|          | 21.0±0.4ms           | 21.1±0.3ms          |    1    | requirement.TimeRequirementSuite.time_constructor [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]    |
|          | 21.1±0.07ms          | 21.1±0.2ms          |    1    | requirement.TimeRequirementSuite.time_constructor [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]    |
| -        | 1.15±0.01ms          | 1.05±0.01ms         |    0.91 | resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]       |
|          | 787±3μs              | 721±2μs             |    0.92 | resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]       |
|          | 824±20μs             | 779±5μs             |    0.94 | resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]       |
|          | 786±6μs              | 724±2μs             |    0.92 | resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]       |
|          | 763±10μs             | 716±3μs             |    0.94 | resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]       |
|          | 1.22±0.01ms          | 1.11±0.01ms         |    0.91 | resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]        |
|          | 1.20±0.02ms          | 1.09±0.01ms         |    0.91 | resolver.TimeResolverSuite.time_resolver_loop [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]        |
|          | 2.16±0.02ms          | 2.17±0.01ms         |    1    | specifiers.TimeSpecSuite.time_constructor [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]           |
|          | 1.85±0.01ms          | 1.87±0.02ms         |    1.01 | specifiers.TimeSpecSuite.time_constructor [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]           |
|          | 2.10±0.02ms          | 2.09±0.01ms         |    1    | specifiers.TimeSpecSuite.time_constructor [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]           |
|          | 1.96±0.01ms          | 1.99±0.02ms         |    1.01 | specifiers.TimeSpecSuite.time_constructor [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]           |
|          | 1.98±0.01ms          | 1.98±0.02ms         |    1    | specifiers.TimeSpecSuite.time_constructor [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]           |
|          | 2.19±0.03ms          | 2.21±0.01ms         |    1.01 | specifiers.TimeSpecSuite.time_constructor [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]            |
|          | 2.11±0.01ms          | 2.12±0.01ms         |    1.01 | specifiers.TimeSpecSuite.time_constructor [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]            |
|          | 9.06±0.09ms          | 9.48±0.06ms         |    1.05 | specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]         |
|          | 6.10±0.05ms          | 6.45±0.04ms         |    1.06 | specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]         |
|          | 6.60±0.07ms          | 6.92±0.03ms         |    1.05 | specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]         |
|          | 6.33±0.04ms          | 6.66±0.03ms         |    1.05 | specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]         |
|          | 6.22±0.1ms           | 6.70±0.08ms         |    1.08 | specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]         |
|          | 9.43±0.08ms          | 9.68±0.1ms          |    1.03 | specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]          |
|          | 9.19±0.04ms          | 9.46±0.05ms         |    1.03 | specifiers.TimeSpecSuite.time_contains_cold [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]          |
| -        | 5.01±0.02ms          | 2.67±0.02ms         |    0.53 | specifiers.TimeSpecSuite.time_contains_complex_warm [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0] |
| -        | 3.64±0.02ms          | 1.96±0.03ms         |    0.54 | specifiers.TimeSpecSuite.time_contains_complex_warm [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0] |
| -        | 3.96±0.03ms          | 2.17±0.02ms         |    0.55 | specifiers.TimeSpecSuite.time_contains_complex_warm [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0] |
| -        | 3.64±0.03ms          | 1.93±0.02ms         |    0.53 | specifiers.TimeSpecSuite.time_contains_complex_warm [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0] |
| -        | 3.66±0.03ms          | 1.96±0.01ms         |    0.53 | specifiers.TimeSpecSuite.time_contains_complex_warm [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0] |
| -        | 5.27±0.04ms          | 2.86±0.05ms         |    0.54 | specifiers.TimeSpecSuite.time_contains_complex_warm [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]  |
| -        | 5.14±0.03ms          | 2.74±0.04ms         |    0.53 | specifiers.TimeSpecSuite.time_contains_complex_warm [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]  |
|          | 6.61±0.06ms          | 6.49±0.1ms          |    0.98 | specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]         |
|          | 4.71±0.03ms          | 4.43±0.03ms         |    0.94 | specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]         |
|          | 5.00±0.06ms          | 4.79±0.05ms         |    0.96 | specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]         |
|          | 4.75±0.03ms          | 4.47±0.04ms         |    0.94 | specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]         |
|          | 4.77±0.06ms          | 4.55±0.02ms         |    0.95 | specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]         |
|          | 6.95±0.06ms          | 6.69±0.04ms         |    0.96 | specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]          |
|          | 6.70±0.05ms          | 6.47±0.01ms         |    0.97 | specifiers.TimeSpecSuite.time_contains_warm [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]          |
| -        | 3.66±0.07ms          | 2.09±0.02ms         |    0.57 | specifiers.TimeSpecSuite.time_filter_complex_cold [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]   |
| -        | 2.58±0.02ms          | 1.52±0.01ms         |    0.59 | specifiers.TimeSpecSuite.time_filter_complex_cold [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]   |
| -        | 2.93±0.02ms          | 1.75±0.01ms         |    0.6  | specifiers.TimeSpecSuite.time_filter_complex_cold [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]   |
| -        | 2.61±0.03ms          | 1.53±0.02ms         |    0.58 | specifiers.TimeSpecSuite.time_filter_complex_cold [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]   |
| -        | 2.65±0.04ms          | 1.54±0.01ms         |    0.58 | specifiers.TimeSpecSuite.time_filter_complex_cold [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]   |
| -        | 3.85±0.05ms          | 2.17±0.01ms         |    0.56 | specifiers.TimeSpecSuite.time_filter_complex_cold [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]    |
| -        | 3.69±0.01ms          | 2.09±0.04ms         |    0.57 | specifiers.TimeSpecSuite.time_filter_complex_cold [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]    |
| -        | 3.63±0.04ms          | 2.05±0.03ms         |    0.56 | specifiers.TimeSpecSuite.time_filter_complex_warm [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]   |
| -        | 2.58±0.03ms          | 1.51±0.01ms         |    0.58 | specifiers.TimeSpecSuite.time_filter_complex_warm [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]   |
| -        | 2.92±0.03ms          | 1.72±0.01ms         |    0.59 | specifiers.TimeSpecSuite.time_filter_complex_warm [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]   |
| -        | 2.57±0.03ms          | 1.47±0.01ms         |    0.57 | specifiers.TimeSpecSuite.time_filter_complex_warm [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]   |
| -        | 2.65±0.04ms          | 1.50±0.01ms         |    0.57 | specifiers.TimeSpecSuite.time_filter_complex_warm [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]   |
| -        | 3.83±0.04ms          | 2.14±0.02ms         |    0.56 | specifiers.TimeSpecSuite.time_filter_complex_warm [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]    |
| -        | 3.65±0.02ms          | 2.04±0.02ms         |    0.56 | specifiers.TimeSpecSuite.time_filter_complex_warm [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]    |
|          | 18.0±0.1μs           | 18.3±0.2μs          |    1.02 | specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]    |
|          | 11.9±0.07μs          | 12.0±0.07μs         |    1    | specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]    |
|          | 10.7±0.05μs          | 10.6±0.02μs         |    0.99 | specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]    |
|          | 10.1±0.05μs          | 10.1±0.09μs         |    1    | specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]    |
|          | 9.90±0.05μs          | 9.86±0.1μs          |    1    | specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]    |
|          | 19.6±0.2μs           | 19.5±0.2μs          |    0.99 | specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]     |
|          | 19.2±0.2μs           | 19.3±0.2μs          |    1.01 | specifiers.TimeSpecSuite.time_filter_simple_cold [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]     |
|          | 15.4±0.2μs           | 15.2±0.1μs          |    0.99 | specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]    |
|          | 10.3±0.04μs          | 10.2±0.07μs         |    0.99 | specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]    |
|          | 8.67±0.06μs          | 8.49±0.04μs         |    0.98 | specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]    |
|          | 8.37±0.04μs          | 8.49±0.06μs         |    1.01 | specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]    |
|          | 8.20±0.1μs           | 8.13±0.05μs         |    0.99 | specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]    |
|          | 16.9±0.3μs           | 16.5±0.04μs         |    0.98 | specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]     |
|          | 16.2±0.2μs           | 16.1±0.09μs         |    1    | specifiers.TimeSpecSuite.time_filter_simple_warm [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]     |
|          | 4.99±0.08μs          | 5.05±0.03μs         |    1.01 | utils.TimeUtils.time_canonicalize_name [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]              |
|          | 3.44±0.02μs          | 3.46±0.01μs         |    1.01 | utils.TimeUtils.time_canonicalize_name [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]              |
|          | 3.64±0.03μs          | 3.63±0.06μs         |    1    | utils.TimeUtils.time_canonicalize_name [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]              |
|          | 3.72±0.04μs          | 3.74±0.04μs         |    1    | utils.TimeUtils.time_canonicalize_name [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]              |
|          | 4.14±0.02μs          | 4.11±0.02μs         |    0.99 | utils.TimeUtils.time_canonicalize_name [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]              |
|          | 5.59±0.08μs          | 5.52±0.06μs         |    0.99 | utils.TimeUtils.time_canonicalize_name [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]               |
|          | 5.41±0.01μs          | 5.43±0.04μs         |    1    | utils.TimeUtils.time_canonicalize_name [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]               |
|          | 2.68±0.03ms          | 2.71±0.03ms         |    1.01 | version.TimeVersionSuite.time_constructor [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]           |
|          | 1.72±0.01ms          | 1.75±0.01ms         |    1.02 | version.TimeVersionSuite.time_constructor [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]           |
|          | 2.13±0.01ms          | 2.14±0.01ms         |    1    | version.TimeVersionSuite.time_constructor [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]           |
|          | 1.95±0.02ms          | 1.95±0.01ms         |    1    | version.TimeVersionSuite.time_constructor [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]           |
|          | 1.73±0.02ms          | 1.72±0.01ms         |    1    | version.TimeVersionSuite.time_constructor [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]           |
|          | 2.88±0.03ms          | 2.86±0.01ms         |    1    | version.TimeVersionSuite.time_constructor [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]            |
|          | 2.76±0.01ms          | 2.78±0.02ms         |    1.01 | version.TimeVersionSuite.time_constructor [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]            |
|          | 4.53±0.04ms          | 4.49±0.02ms         |    0.99 | version.TimeVersionSuite.time_hash [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]                  |
|          | 2.94±0.01ms          | 2.97±0.01ms         |    1.01 | version.TimeVersionSuite.time_hash [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]                  |
|          | 3.59±0.03ms          | 3.56±0.01ms         |    0.99 | version.TimeVersionSuite.time_hash [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]                  |
|          | 3.31±0.01ms          | 3.34±0.01ms         |    1.01 | version.TimeVersionSuite.time_hash [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]                  |
|          | 3.25±0.03ms          | 3.23±0.02ms         |    0.99 | version.TimeVersionSuite.time_hash [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]                  |
|          | 4.84±0.06ms          | 4.76±0.03ms         |    0.98 | version.TimeVersionSuite.time_hash [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]                   |
|          | 4.59±0.03ms          | 4.62±0.02ms         |    1.01 | version.TimeVersionSuite.time_hash [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]                   |
|          | 2.79±0.01ms          | 2.78±0.02ms         |    1    | version.TimeVersionSuite.time_sort_cold [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]             |
|          | 2.04±0.02ms          | 2.03±0.01ms         |    1    | version.TimeVersionSuite.time_sort_cold [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]             |
|          | 2.26±0.01ms          | 2.26±0.02ms         |    1    | version.TimeVersionSuite.time_sort_cold [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]             |
|          | 2.35±0.01ms          | 2.33±0.02ms         |    0.99 | version.TimeVersionSuite.time_sort_cold [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]             |
|          | 2.34±0.01ms          | 2.35±0.02ms         |    1    | version.TimeVersionSuite.time_sort_cold [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]             |
|          | 3.52±0.02ms          | 3.55±0.01ms         |    1.01 | version.TimeVersionSuite.time_sort_cold [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]              |
|          | 3.28±0.02ms          | 3.29±0.04ms         |    1    | version.TimeVersionSuite.time_sort_cold [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]              |
|          | 2.22±0.01ms          | 2.22±0.02ms         |    1    | version.TimeVersionSuite.time_sort_warm [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]             |
|          | 1.67±0.01ms          | 1.66±0ms            |    1    | version.TimeVersionSuite.time_sort_warm [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]             |
|          | 1.90±0.04ms          | 1.88±0ms            |    0.99 | version.TimeVersionSuite.time_sort_warm [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]             |
|          | 1.97±0.02ms          | 1.94±0.01ms         |    0.99 | version.TimeVersionSuite.time_sort_warm [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]             |
|          | 1.96±0.01ms          | 1.97±0.02ms         |    1    | version.TimeVersionSuite.time_sort_warm [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]             |
|          | 2.83±0.01ms          | 2.87±0.02ms         |    1.01 | version.TimeVersionSuite.time_sort_warm [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]              |
|          | 2.57±0.01ms          | 2.55±0.01ms         |    0.99 | version.TimeVersionSuite.time_sort_warm [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]              |
|          | 4.47±0.1ms           | 4.38±0.04ms         |    0.98 | version.TimeVersionSuite.time_str [runnervm0kj6c/virtualenv-py3.10-PYTHONHASHSEED0]                   |
|          | 3.13±0.02ms          | 3.15±0.02ms         |    1.01 | version.TimeVersionSuite.time_str [runnervm0kj6c/virtualenv-py3.11-PYTHONHASHSEED0]                   |
|          | 3.55±0.03ms          | 3.54±0.03ms         |    1    | version.TimeVersionSuite.time_str [runnervm0kj6c/virtualenv-py3.12-PYTHONHASHSEED0]                   |
|          | 3.14±0.01ms          | 3.20±0.02ms         |    1.02 | version.TimeVersionSuite.time_str [runnervm0kj6c/virtualenv-py3.13-PYTHONHASHSEED0]                   |
|          | 3.08±0.05ms          | 3.07±0.02ms         |    1    | version.TimeVersionSuite.time_str [runnervm0kj6c/virtualenv-py3.14-PYTHONHASHSEED0]                   |
|          | 4.88±0.03ms          | 4.86±0.01ms         |    1    | version.TimeVersionSuite.time_str [runnervm0kj6c/virtualenv-py3.8-PYTHONHASHSEED0]                    |
|          | 4.70±0.02ms          | 4.71±0.03ms         |    1    | version.TimeVersionSuite.time_str [runnervm0kj6c/virtualenv-py3.9-PYTHONHASHSEED0]                    |

SOME BENCHMARKS HAVE CHANGED SIGNIFICANTLY.
PERFORMANCE INCREASED.

@notatallshaw

Copy link
Copy Markdown
Member Author

We could also take this further in the future, and implement a more complex optimizer, for example if there are two specifiers >6.0 and >5.0 would could discard >5.0 from needing to run.

However there will be more complex trade offs there of cost to analyze vs. cost to run.

@notatallshaw
notatallshaw merged commit 8810e9e into pypa:main Mar 8, 2026
56 checks passed
ngoldbaum pushed a commit to ngoldbaum/packaging that referenced this pull request Apr 1, 2026
This was referenced Apr 13, 2026
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.

2 participants