| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
@Taeknology Can you share a benchmark script (pyperf based, with the most relevant benchmark(s)) for the PR together with your results? |
Sorry, something went wrong.
|
Sure. I reran this with a pyperf script against the exact PR base The three cases keep the input at 1 MiB while varying the number of lines, so Regular GIL
Free-threaded
The individual free-threaded forward/reverse runs had geometric means of 1.14x """pyperf benchmarks for the result-list construction in BytesIO.readlines()."""
from __future__ import annotations
import io
import pyperf
# Keep each input close to 1 MiB while varying the number of result-list
# appends. The long-line case is a useful control: scanning and copying bytes
# dominates there, so a list-append optimization should have less impact.
CASES = {
"bytesio_readlines_131072x8": (131_072, 8),
"bytesio_readlines_16384x64": (16_384, 64),
"bytesio_readlines_1024x1024": (1_024, 1_024),
}
def make_data(line_count: int, line_size: int) -> bytes:
line = (b"x" * (line_size - 1)) + b"\n"
data = line * line_count
assert len(data) == line_count * line_size
return data
def main() -> None:
runner = pyperf.Runner()
runner.metadata["description"] = (
"BytesIO.readlines() over equally sized inputs with different line counts"
)
for name, (line_count, line_size) in CASES.items():
stream = io.BytesIO(make_data(line_count, line_size))
runner.timeit(
name,
stmt="stream.seek(0); stream.readlines()",
globals={"stream": stream},
metadata={
"line_count": line_count,
"line_size": line_size,
"input_size": line_count * line_size,
},
)
if __name__ == "__main__":
main()Example command: python bench_bytesio_readlines.py \
--affinity=2 --processes=25 --values=5 --warmups=2 \
-o result.json
python -m pyperf compare_to base.json patched.json \
--table --table-format mdpyperf check still reports “not enough samples” warnings for several cases. These reruns are lower than the earlier author-reported figures, so I have not Sanitized pyperf JSON files used for the tables above (host-identifying and unrelated runtime-state metadata removed; timing samples, warmups, and retained reproducibility metadata unchanged): pr-155171-pyperf-results-sanitized.zip |
Sorry, something went wrong.
There was a problem hiding this comment.
The numbers for the free-threading build I can reproduce on my system (a bit faster even). The benchmark does not include io to disk or network, but this is still a nice improvement.
Sorry, something went wrong.
|
Thanks for the reviews! Since it's been a little while, I'm just checking in: is there anything else needed from my side before this can be merged? |
Sorry, something went wrong.
|
@Taeknology Thanks for the PR, if no other comments come in I will merge in a few days. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Summary
Use _PyList_AppendTakeRef() while building the private result list in
BytesIO.readlines().
The new bytes object is transferred directly to the result list, avoiding the
incref/decref pair performed by PyList_Append(). The result list is freshly
allocated, remains local for the complete loop, and is protected by the
BytesIO critical section until it is returned.
Partially fixes gh-150942.
Benchmark
Author-run pyperf comparisons used five BytesIO.readlines() workloads on a
free-threaded Clang 21 PGO+ThinLTO build. Two balanced baseline/patch comparison
pairs reported improvements of 8.64% to 36.18% across the workloads, with
19.27% and 20.43% equal-weight geometric-mean improvements. Earlier regular-GIL
PGO+LTO comparisons reported 7.64% and 8.68% geometric-mean improvements.
Several individual suites produced pyperf check stability warnings. The raw
benchmark script and result files are not attached to this PR, so these figures
are author-reported results and are not independently reproducible from the
public PR artifacts.
Verification
All required GitHub checks pass on the current head, including regular and
free-threaded builds and tests, sanitizer jobs, and generated-file checks.
git diff --check also passes for the complete two-file diff.