Skip to content

perf(capture): skip reading the capture file when nothing was written#14687

Open
henryiii wants to merge 2 commits into
pytest-dev:mainfrom
henryiii:capture-snap-fastpath
Open

perf(capture): skip reading the capture file when nothing was written#14687
henryiii wants to merge 2 commits into
pytest-dev:mainfrom
henryiii:capture-snap-fastpath

Conversation

@henryiii

@henryiii henryiii commented Jul 7, 2026

Copy link
Copy Markdown

I was investigating pytest performance on pypa/packaging's test suite, which has a lot of small tests, and pytest takes quite a bit of time. I used python 3.15's new sampling profiler, setup something like this:

cd <pytest>
uv sync --python 3.15
uv pip install -e <packaging> hypothesis pretend tomli-w
cd <packaging>
sudo <pytest>/.venv/bin/python -m profiling.sampling run -m pytest

I then feed the report into Claude and had it look for spots that could be made faster. This is the first thing it found.

FDCapture.snap() did seek/read/seek/truncate even when the test produced no output. Since the tmpfile is unbuffered, a single fstat can prove it is empty and skip all of that.

This removes ~6 of these cycles per test (2 streams x 3 phases); on pypa/packaging (62k tests) it cuts the total runtime by about 8% (the overhead from pytest something like 25%, I think).

I used an Assisted-by trailer (as recommended by the linux kernel) instead of a Co-authored-by trailer, I can switch if you prefer; generally Co-authored-by seems to be discouraged due to the implied copyright ownership. I rewrote the comments, so it's 33/67 me and Claude.

There isn't an associated issue, so will do a changelog with the PR number if that's fine.

Assisted-by: ClaudeCode:claude-fable-5

henryiii added 2 commits July 7, 2026 10:16
FDCapture.snap() did seek/read/seek/truncate through the TextIOWrapper on
every test phase even when the test produced no output. Since the tmpfile
is unbuffered, a single fstat can prove it is empty and skip all of that.

For suites of many small quiet tests this removes ~6 of these cycles per
test (2 streams x 3 phases); on pypa/packaging (62k tests) it cuts the
total runtime by about 8%.

Assisted-by: ClaudeCode:claude-fable-5
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Assisted-by: ClaudeCode:claude-fable-5
@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided (automation) changelog entry is part of PR label Jul 7, 2026
@henryiii henryiii marked this pull request as ready for review July 7, 2026 14:59
@nicoddemus

Copy link
Copy Markdown
Member

n pypa/packaging (62k tests) it cuts the total runtime by about 8% (the overhead from pytest something like 25%, I think).

That's substantial. I'm surprised those seek/truncate calls are that "expensive" on an empty file.

There isn't an associated issue, so will do a changelog with the PR number if that's fine.

Yes that's perfectly fine, thanks.

@Pierre-Sassoulas Pierre-Sassoulas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, I can reproduce locally, looking at the cprofile, it seems like it also eliminate calls to <method 'truncate' of '_io.TextIOWrapper' objects>. It's small gain for pytest that has a few assert True calls (0.24% probably within noise) but become significant if you construct code that exercise the costly path.

@Pierre-Sassoulas Pierre-Sassoulas added the type: performance performance or memory problem/improvement label Jul 7, 2026

@RonnyPfannschmidt RonnyPfannschmidt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice find thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided (automation) changelog entry is part of PR type: performance performance or memory problem/improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants