GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-155742: Use PyBytesWriter in zlib by vstinner · Pull Request #157348 · python/cpython

Conversation

@vstinner

@vstinner vstinner commented Sep 12, 2026 •
edited by bedevere-app Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Replace soft deprecated _PyBytes_Resize() with PyBytesWriter.

  • Issue: Treat bytes objects as immutable in C code #155742

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@vstinner
pythongh-155742: Use PyBytesWriter in zlib
44a2974
Replace soft deprecated _PyBytes_Resize() with PyBytesWriter.
@vstinner vstinner added the skip news label Sep 12, 2026
@bedevere-app bedevere-app Bot mentioned this pull request Sep 12, 2026
Treat bytes objects as immutable in C code #155742
Closed
@vstinner

vstinner commented Sep 12, 2026

Copy link
Copy Markdown
Member Author

I marked the PR as a draft since benchmarks should be run on this change.

_PyBytes_Resize(n) allocates exactly n bytes, whereas PyBytesWriter_Resize(n) overallocates the buffer.

See also PR gh-139976 which modified _bz2, _lzma, _zstd and zlib extensions to use PyBytesWriter (in pycore_blocks_output_buffer.h).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Nov 8, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

@emmatyping: Do you have advices on what I should measure to check that the change doesn't introduce performance regression? Do you have maybe an existing benchmark?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@emmatyping

emmatyping commented Nov 9, 2026

Copy link
Copy Markdown
Member

With #161478 I'm thinking of doing a deeper dive into why the behavior is different between glibc Linux, macOS, and Windows for PyBytesWriter. I'd like to fix this in PyBytesWriter so that is performant across all platforms. I'll follow up here with findings/info.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@rhpvorderman

rhpvorderman commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

I just made a comment in referenced issue. Performance is better on all platforms. Just only in situations were the user actually cares about performance and chooses small cache-local output blocks and streaming. Is performance important when the user does not care/know about how detrimental (de)compressing everything into memory at once is?

Regrowing an in-memory buffer has a trade-off. Either the allocated buffer fits, and it can be repurposed to a bytes object and thus is very fast. Or it doesn't and realloc is called. In that case the memory might need to be moved requiring a memcpy call. The problem on multiple reallocations is that this requires multiple memcpy calls, and the same bit of memory at the beginning of the buffer is copied multiple times. That is a waste. The previous block allocation mechanism prevented this by filling several blocks which did not need to be resized and then memcpy'ing them into the output. This means all filled op memory is only memcpy'ed once. This is a lot faster for bigger in-memory outputs. The disadvantage is the overhead of such a system, which makes it slower for smaller outputs.

For python-isal (zlib/gzip-compatible library, but faster) I choose the growth of in-memory buffer because little overhead for small sizes rewards good cpu-cache local use of small buffers and I can assume that when you are using python-isal, you actually care about that. For CPython, I am not so sure. Python is a beginner-friendly language, so I assume many people will do the easy thing, rather than the more convoluted thing that is actually better for the cpu cache.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
Merge branch 'main' into zlib_writer
6ca6b80
@vstinner

vstinner commented Nov 9, 2026

Copy link
Copy Markdown
Member Author

This PR changes the allocation strategy: replacing _PyBytes_Resize() with PyBytesWriter_Resize() means that the buffer is overallocated. It can reduce the number of realloc() (good!), but it can also introduce a final realloc() (bad!) in PyBytesWriter_FinishWithPointer(). It depends on the final size.

If we are shy and don't want to take any risk, I can just use the private API and set writer->overallocation = 0; to disable overallocation.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Nov 9, 2026

Copy link
Copy Markdown
Member Author

With #161478 I'm thinking of doing a deeper dive into why the behavior is different between glibc Linux, macOS, and Windows for PyBytesWriter. I'd like to fix this in PyBytesWriter so that is performant across all platforms. I'll follow up here with findings/info.

Yeah, I saw that issue and I'm concerned about the (unexpected) worse performance. I agree that if a solution is designed, it would be better to implement it directly in PyBytesWriter, so all PyBytesWriter users would benefit from it. (Well, let's continue the discussion in #161478.)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@maikschulze

maikschulze commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

@vstinner, I already stumbled upon this issue with the final realloc() on macOS, for large outputs (4 MiB and up). Once a block is shrunk in place, it's too small for the next call's request, so malloc can't reuse it. Every call then gets fresh memory and faults it all in, and the footprint keeps growing. For example, PyBytesWriter_Create(bound) + PyBytesWriter_FinishWithSize(n) with a compression bound and 64 MiB of incompressible data takes 3.2 ms per call (4,099 page faults). If the block is kept instead, it's 0.97 ms, with no page faults.

What helped in my tests is keeping the block in PyBytesWriter_FinishWithSize() when only a small tail is unused, which would fix it for every PyBytesWriter user with that pattern. I can post the details in a separate issue tomorrow.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

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

Reviewers

@StanFromIreland StanFromIreland Awaiting requested review from StanFromIreland StanFromIreland will be requested when the pull request is marked ready for review StanFromIreland is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

skip news

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@vstinner @emmatyping @rhpvorderman @maikschulze
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.