Skip to content

Commit d2c280b

Browse files
authored
Improve Performance of Picking Best Candidate from Indexes
Use a mapping for random lookup instead of list traversal.
1 parent 34fbe69 commit d2c280b

File tree

4 files changed

+35
-4
lines changed

4 files changed

+35
-4
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,6 @@ tests/data/common_wheels/
4848

4949
# Mac
5050
.DS_Store
51+
52+
# Profiling related artifacts
53+
*.prof

news/9748.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve performance when picking the best file from indexes during `pip install`.

src/pip/_internal/index/package_finder.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,12 @@ def __init__(
434434
self._project_name = project_name
435435
self._specifier = specifier
436436
self._supported_tags = supported_tags
437+
# Since the index of the tag in the _supported_tags list is used
438+
# as a priority, precompute a map from tag to index/priority to be
439+
# used in wheel.find_most_preferred_tag.
440+
self._wheel_tag_preferences = {
441+
tag: idx for idx, tag in enumerate(supported_tags)
442+
}
437443

438444
def get_applicable_candidates(
439445
self,
@@ -512,14 +518,17 @@ def _sort_key(self, candidate):
512518
if link.is_wheel:
513519
# can raise InvalidWheelFilename
514520
wheel = Wheel(link.filename)
515-
if not wheel.supported(valid_tags):
521+
try:
522+
pri = -(wheel.find_most_preferred_tag(
523+
valid_tags, self._wheel_tag_preferences
524+
))
525+
except ValueError:
516526
raise UnsupportedWheel(
517527
"{} is not a supported wheel for this platform. It "
518528
"can't be sorted.".format(wheel.filename)
519529
)
520530
if self._prefer_binary:
521531
binary_preference = 1
522-
pri = -(wheel.support_index_min(valid_tags))
523532
if wheel.build_tag is not None:
524533
match = re.match(r'^(\d+)(.*)$', wheel.build_tag)
525534
build_tag_groups = match.groups()

src/pip/_internal/models/wheel.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name that have meaning.
33
"""
44
import re
5-
from typing import List
5+
from typing import Dict, List
66

77
from pip._vendor.packaging.tags import Tag
88

@@ -66,8 +66,26 @@ def support_index_min(self, tags):
6666
"""
6767
return min(tags.index(tag) for tag in self.file_tags if tag in tags)
6868

69+
def find_most_preferred_tag(self, tags, tag_to_priority):
70+
# type: (List[Tag], Dict[Tag, int]) -> int
71+
"""Return the priority of the most preferred tag that one of the wheel's file
72+
tag combinations acheives in the given list of supported tags using the given
73+
tag_to_priority mapping, where lower priorities are more-preferred.
74+
75+
This is used in place of support_index_min in some cases in order to avoid
76+
an expensive linear scan of a large list of tags.
77+
78+
:param tags: the PEP 425 tags to check the wheel against.
79+
:param tag_to_priority: a mapping from tag to priority of that tag, where
80+
lower is more preferred.
81+
82+
:raises ValueError: If none of the wheel's file tags match one of
83+
the supported tags.
84+
"""
85+
return min(tag_to_priority[tag] for tag in self.file_tags if tag in tag_to_priority)
86+
6987
def supported(self, tags):
70-
# type: (List[Tag]) -> bool
88+
# type: (Iterable[Tag]) -> bool
7189
"""Return whether the wheel is compatible with one of the given tags.
7290
7391
:param tags: the PEP 425 tags to check the wheel against.

0 commit comments

Comments
 (0)