Skip to content

Performance regression in BufferedReader.readline() after switching to PyBytesWriter #158897

Description

@leandrodamascena

Bug report

Bug description:

I have a project where I build CPython into OCI images and run them on AWS Lambda to catch performance changes, and it flagged a regression in BufferedReader.readline() after #158615, which switched it to PyBytesWriter. It shows up when lines don't fit in the buffer and readline() has to refill it:

import io

BUFFER_SIZE = 4096

medium_a = b"x" * 4080 + b"abc\n"        # 4,084 bytes
medium_b = b"y" * 4112 + b"def\n"        # 4,116 bytes
long_line = b"z" * 40960 + b"trailer\n"  # 40,968 bytes

data = (medium_a + medium_b + long_line) * 64

reader = io.BufferedReader(io.BytesIO(data), buffer_size=BUFFER_SIZE)
while reader.readline():
    pass

The regression is real and repeatable, but how big it is depends a lot on where it runs. The CPU, the allocator and the build options all seem to matter. I tested it in a few different environments, comparing #158615 with its parent:

Environment PGO+LTO Result
x86_64 VM, mixed lines yes ~43% slower (also in reverse order, and in Docker with the same images)
x86_64 VM, mixed lines no 18% slower
x86_64 VM, long lines only no 23% slower
x86_64 VM, medium lines only no 16% faster
x86_64 host no ~7% faster
arm64 VM yes / no no significant change
arm64 host yes / no faster

This also explains why the benchmark in #158615 didn't show any impact: on some machines the new code is as fast or faster. On the x86_64 host where I could run perf, the profile changes as expected: the parent spends more time in bytes_join and memmove, and the new code shows PyBytesWriter_WriteBytes, buffer growth and realloc. My guess is that the growth and realloc path is what gets more expensive in the environments where it regresses, but I couldn't prove that.

To be fair to the change, peak memory in this scenario went down from 89.7 KiB to 53.6 KiB (measured with tracemalloc), which is a nice improvement.

Since the regression depends on the environment, it can easily go unnoticed in a single benchmark, so I thought it was worth reporting. Happy to run more tests or try a patch. cc @vstinner

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

No activity

Activity on this issue will appear here.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirperformancePerformance or resource usage

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions