Skip to content

Ask the zip archive whether a member name repeats, instead of walking every member - #237

Merged
abrignoni merged 1 commit into
mainfrom
perf/zip-seeker-skips-the-member-walk
Sep 20, 2026
Merged

abrignoni merged 1 commit into
mainfrom
perf/zip-seeker-skips-the-member-walk

Conversation

@abrignoni

@abrignoni abrignoni commented Sep 20, 2026

Copy link
Copy Markdown
Owner

The zip seeker grouped every member of an archive into a dict of name to entries, purely to find the names stored more than once. ZipFile has already keyed its own NameToInfo on the member name by the time the seeker runs, so that dict is short by exactly the number of repeats. Comparing two lengths answers the same question and allocates nothing.

Measured on a 630,560 member extraction, where the walk cost a dict entry, a list and a tuple per member:

before after
the member walk 138 MB not run
peak, light run over that archive 987 MB 871 MB
peak, run that also parses every journal string 1,313 MB 1,277 MB

Three runs a side on the light profile: 1018, 985, 987 against 871, 871, 871. Row counts are unchanged, and the artifact that reads 1,208,783 rows from that archive still reads 1,208,783.

Checked on two more real archives, through the seeker itself rather than a full run:

archive members repeated names peak, main peak, branch
Android full filesystem 209,902 0 283 MB 235 MB
Android full filesystem 82,679 87 130 MB 130 MB

The second takes the walk, as it must, and both trees return the same 82,592 distinct names, the same 53 chosen entries and the same 53 names carrying another version.

Archives that do repeat a name take the walk exactly as before. NameToInfo is not part of zipfile's documented surface, so an absent one falls through to the walk rather than assuming anything. It is present on 3.10 through 3.14.

Four tests cover the new branch. One makes the member walk raise, so the cheap path cannot be dropped without a failure, and its control does the same on a repeated name where the walk must still run. Two mutants were used: removing the cheap path fails exactly the walk test, and taking it unconditionally fails nine of the repeated-name tests.

The seeker function and its test file are byte-identical across the five cores, and stay so after this change.

🤖 Generated with Claude Code

… every member

The seeker grouped all of an archive's members into a dict of name to entries purely to
find the names stored more than once. ZipFile has already keyed its own NameToInfo on the
member name by the time the seeker runs, so that dict is short by exactly the number of
repeats. Comparing two lengths answers the same question and allocates nothing.

Measured on a 630,560 member extraction: the walk cost a dict entry, a list and a tuple
per member, 138 MB, to discover that no name repeated. Peak memory for a light run over
that archive goes from 987 MB to 871 MB. Row counts are unchanged.

Archives that do repeat a name still take the walk and are unaffected. Four tests cover
the new branch, including one that makes the member walk raise so the cheap path cannot
be dropped without a failure, and its control on a repeated name where the walk must run.

The seeker and its test are byte-identical across the five cores, and stay so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@abrignoni
abrignoni merged commit 1f63cd5 into main Sep 20, 2026
8 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.

1 participant