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-140135: use `PyBytesWriter` in `io.RawIOBase.readall`; 3.98x faster by maurycy · Pull Request #140139 · python/cpython

Conversation

@maurycy

@maurycy maurycy commented Oct 14, 2025 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor

The issue #140135 provides more details.

Benchmark

The script:
import io
import pyperf

CHUNK_SIZE = 4096
SIZES = [1, 4, 8, 16, 32, 64, 128]


class ChunkedRaw(io.RawIOBase):
    def __init__(self, data, chunk_size):
        self._buf = memoryview(data)
        self._pos = 0
        self._chunk_size = chunk_size

    def readable(self):
        return True

    def read(self, n: int = -1):
        if self._pos >= len(self._buf):
            return b""

        to_read = (
            self._chunk_size
            if (n is None or n < 0)
            else min(n, self._chunk_size)
        )

        end = min(self._pos + to_read, len(self._buf))
        out = bytes(self._buf[self._pos : end])
        self._pos = end

        return out


def generate_bytes(total):
    block = b"abcdefghijklmnopqrstuvwxyz0123456789" * 128
    return (block * (total // len(block) + 1))[:total]


def _bench_readall(data, chunk_size):
    r = ChunkedRaw(data, chunk_size)
    out = r.readall()
    if len(out) != len(data):
        raise RuntimeError("what is going on...???")


def main():
    runner = pyperf.Runner()
    for size_mib in SIZES:
        total_bytes = size_mib * 1024 * 1024
        data = generate_bytes(total_bytes)
        name = f"rawiobase_readall_{size_mib}MB_chunk{CHUNK_SIZE}"
        runner.bench_func(name, _bench_readall, data, CHUNK_SIZE)


if __name__ == "__main__":
    main()

The results (with --rigorous):

Benchmark main pybytes-iobase-readall
rawiobase_readall_1MB_chunk4096 673 us 111 us: 6.04x faster
rawiobase_readall_4MB_chunk4096 2.49 ms 434 us: 5.74x faster
rawiobase_readall_8MB_chunk4096 5.21 ms 907 us: 5.75x faster
rawiobase_readall_16MB_chunk4096 12.1 ms 2.45 ms: 4.95x faster
rawiobase_readall_32MB_chunk4096 25.1 ms 10.2 ms: 2.45x faster
rawiobase_readall_64MB_chunk4096 51.0 ms 20.4 ms: 2.50x faster
rawiobase_readall_128MB_chunk4096 101 ms 38.9 ms: 2.60x faster
Geometric mean (ref) 3.98x faster

The environment:

% ./python -c "import sysconfig; print(sysconfig.get_config_var('CONFIG_ARGS'))"
'--enable-optimizations' '--with-lto'

sudo ./python -m pyperf system tune ensured.

  • Issue: Use PyBytesWriter API in io.RawIOBase.readall #140135

Sorry, something went wrong.

Uh oh!

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

yihong0618 reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
@maurycy
use PyBytesWriter in _io__RawIOBase_readall_impl
f669add
@bedevere-app bedevere-app Bot added the awaiting review label Oct 14, 2025
@bedevere-app bedevere-app Bot mentioned this pull request Oct 14, 2025
Use PyBytesWriter API in io.RawIOBase.readall #140135
Closed
@maurycy maurycy changed the title gh-140135: use PyBytesWriter in io.RawIOBase.readall, 2.34x faster gh-140135: use PyBytesWriter in io.RawIOBase.readall Oct 14, 2025
@maurycy
unused var
eab9742
@maurycy maurycy changed the title gh-140135: use PyBytesWriter in io.RawIOBase.readall gh-140135: use PyBytesWriter in io.RawIOBase.readall; 3.98x faster Oct 15, 2025
vstinner
vstinner reviewed Oct 15, 2025
View reviewed changes

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Please add a NEWS entry, IMO this speedup is significant enough to be documented!

Sorry, something went wrong.

Uh oh!

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

All reactions
@vstinner

vstinner commented Oct 15, 2025

Copy link
Copy Markdown
Member

Impressive speedup!

maurycy reacted with heart emoji
All reactions
  • ❤️ 1 reaction

Sorry, something went wrong.

Uh oh!

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

cmaloney
cmaloney reviewed Oct 15, 2025
View reviewed changes

@cmaloney cmaloney left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Overall looking good

Sorry, something went wrong.

Uh oh!

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

maurycy reacted with heart emoji
All reactions
  • ❤️ 1 reaction
Comment thread Modules/_io/iobase.c Resolved

Uh oh!

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

maurycy added 2 commits October 15, 2025 02:11
@maurycy
be curly
b8a7f89
@maurycy
NEWS
f0ae824
@maurycy

maurycy commented Oct 15, 2025 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor Author

@vstinner

Impressive speedup!

Thank you!

My biggest worry that the benchmark is wrong. I'm more than happy to run a completely different benchmark on the same machine!

Please add a NEWS entry, IMO this speedup is significant enough to be documented!

Done: f0ae824

All reactions

Sorry, something went wrong.

Uh oh!

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

@maurycy

maurycy commented Oct 15, 2025 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor Author

The CI failed. I'm not exactly sure why. The same jobs succeeded before:

  • https://github.com/python/cpython/actions/runs/18513182444/job/52758296689
  • https://github.com/python/cpython/actions/runs/18513182444/job/52758296962

No meaningful changes since the last run (b8a7f89 is just style, and f0ae824 just adds a NEWS entry.)

From what I see, some flakiness was reported:

  • https://discuss.python.org/t/is-the-test-threading-on-python-pr-easy-to-failed/104359/4

I'm rerunning by merging the main branch (not recommended but I couldn't figure out any other way.)

All reactions

Sorry, something went wrong.

Uh oh!

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

@maurycy
Merge branch 'main' into pybytes-iobase-readall
8630390
vstinner
vstinner approved these changes Oct 15, 2025
View reviewed changes

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

LGTM. Nice optimization!

Sorry, something went wrong.

Uh oh!

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

yihong0618 reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
@bedevere-app bedevere-app Bot added awaiting merge and removed awaiting review labels Oct 15, 2025
@emmatyping

emmatyping commented Oct 15, 2025

Copy link
Copy Markdown
Member

I think this looks good, the only other thing is I would verify this doesn't regress performance for sizes <1MB (e.g. 1K and 4K maybe).

I expect the performance differences to be much smaller there if at all present, but that's OK, we just want to make sure they aren't regressing.

maurycy reacted with heart emoji
All reactions
  • ❤️ 1 reaction

Sorry, something went wrong.

Uh oh!

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

@maurycy

maurycy commented Oct 15, 2025 •
edited
Loading

Uh oh!

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

Copy link
Copy Markdown
Contributor Author

@emmatyping

I think this looks good, the only other thing is I would verify this doesn't regress performance for sizes <1MB (e.g. 1K and 4K maybe).

I expect the performance differences to be much smaller there if at all present, but that's OK, we just want to make sure they aren't regressing.

Great point.

The benchmark (the same, just tweaked `SIZES_KB`):
import io
import pyperf

CHUNK_SIZE = 1024
SIZES_KB = [1, 4, 8]


class ChunkedRaw(io.RawIOBase):
    def __init__(self, data, chunk_size):
        self._buf = memoryview(data)
        self._pos = 0
        self._chunk_size = chunk_size

    def readable(self):
        return True

    def read(self, n: int = -1):
        if self._pos >= len(self._buf):
            return b""

        to_read = (
            self._chunk_size
            if (n is None or n < 0)
            else min(n, self._chunk_size)
        )

        end = min(self._pos + to_read, len(self._buf))
        out = bytes(self._buf[self._pos : end])
        self._pos = end

        return out


def generate_bytes(total):
    block = b"abcdefghijklmnopqrstuvwxyz0123456789" * 128
    return (block * (total // len(block) + 1))[:total]


def _bench_readall(data, chunk_size):
    r = ChunkedRaw(data, chunk_size)
    out = r.readall()
    if len(out) != len(data):
        raise RuntimeError("what is going on...???")


def main():
    runner = pyperf.Runner()
    for size_kib in SIZES_KB:
        total_bytes = size_kib * 1024
        data = generate_bytes(total_bytes)
        name = f"rawiobase_readall_{size_kib}KB_chunk{CHUNK_SIZE}"
        runner.bench_func(name, _bench_readall, data, CHUNK_SIZE)


if __name__ == "__main__":
    main()

The result:

Benchmark main pybytes-iobase-readall
rawiobase_readall_1KB_chunk1024 2.51 us 901 ns: 2.79x faster
rawiobase_readall_4KB_chunk1024 5.87 us 2.19 us: 2.68x faster
rawiobase_readall_8KB_chunk1024 9.94 us 3.80 us: 2.62x faster
Geometric mean (ref) 2.69x faster
yihong0618 reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

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

emmatyping
emmatyping approved these changes Oct 15, 2025
View reviewed changes

@emmatyping emmatyping left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Great thank you!

Sorry, something went wrong.

Uh oh!

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

yihong0618 reacted with thumbs up emoji maurycy reacted with heart emoji
All reactions
  • 👍 1 reaction
  • ❤️ 1 reaction
cmaloney
cmaloney approved these changes Oct 15, 2025
View reviewed changes
@maurycy maurycy mentioned this pull request Oct 15, 2025
Use PyBytesWriter API in PEG parser's _build_concatenated_bytes, avoid quadratic memory allocations #140149
Closed
@vstinner
Remove empty line
1b9d655
@vstinner
vstinner enabled auto-merge (squash) October 15, 2025 13:38
@vstinner
vstinner merged commit d301587 into python:main Oct 15, 2025
4 checks passed

Uh oh!

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

@bedevere-app bedevere-app Bot removed the awaiting merge label Oct 15, 2025
@maurycy
maurycy deleted the pybytes-iobase-readall branch October 15, 2025 14:13
@cmaloney cmaloney mentioned this pull request Nov 19, 2025
Reduce copies when reading files in pyio, match behavior of _io #129005
Closed
StanFromIreland pushed a commit to StanFromIreland/cpython that referenced this pull request Dec 6, 2025
@maurycy @StanFromIreland
pythongh-140135: Use PyBytesWriter in io.RawIOBase.readall(); 4x …
819e6e3
…faster (python#140139)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@vstinner vstinner vstinner approved these changes
@cmaloney cmaloney cmaloney approved these changes
@emmatyping emmatyping emmatyping approved these changes

Uh oh!

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

Assignees

No one assigned

Labels

None yet

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

@maurycy @vstinner @emmatyping @cmaloney
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 01402520 — 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.