Skip to content

gh-150942: Speed up BytesIO.readlines() using reference-stealing append - #155171

Open
Taeknology wants to merge 3 commits into
python:mainfrom
Taeknology:perf/gh-150942-bytesio-readlines-final
Open

gh-150942: Speed up BytesIO.readlines() using reference-stealing append#155171
Taeknology wants to merge 3 commits into
python:mainfrom
Taeknology:perf/gh-150942-bytesio-readlines-final

Conversation

@Taeknology

Copy link
Copy Markdown
Contributor

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 cannot be accessed concurrently before it is returned.

Partially fixes gh-150942.

Benchmark

The benchmark uses the five BytesIO.readlines() workload shapes proposed for this issue:

  • 1,000,000 lines, 8-byte payload
  • 500,000 lines, 24-byte payload
  • 100,000 lines, 256-byte payload
  • 2,000,000 empty lines
  • 2,000,000 lines, 8-byte payload

Each result is rigorous pyperf pinned to CPU 2 and executed in baseline-patch-patch-baseline order. Two independent baseline/patch builds used the exact same baseline-generated PGO profile (SHA-256 f9e7f64605c3fb5353369c304f32dce491c4315cb1a2001ea560a9584fbfed26) with Clang 21, PGO, ThinLTO, and a free-threaded build from ce5ae29ef9b8ccb32683c922d135339311c36c9d.

Workload Pair 1 Pair 2
1M × 8 bytes 19.72% faster 18.42% faster
500k × 24 bytes 19.86% faster 18.35% faster
100k × 256 bytes 9.46% faster 8.64% faster
2M × 0 bytes 36.18% faster 35.98% faster
2M × 8 bytes 18.40% faster 16.56% faster
Equal-weight geometric mean 20.43% faster 19.27% faster

Same-variant geometric-mean drift was 0.43% for the baselines and 0.54% for the patched builds. Every workload was significantly faster in both comparisons.

Earlier independent regular-GIL PGO+LTO pairs measured 7.64% and 8.68% geometric-mean improvements, with 0.37% baseline and 0.59% patched-build drift.

pyperf check reports stability warnings for several individual suites. The raw suites are therefore retained and the claim is based on two balanced independent comparisons, the shared PGO profile, low same-variant drift, and consistent significant direction across every workload—not on an unqualified stability claim.

Benchmark script and command:

$ python bench_bytesio_readlines.py --rigorous --affinity 2 -o result.json

Tests

  • Debug regular-GIL python -m test test_io -j2: 1,047 run, 28 skipped, success.
  • Debug free-threaded python -m test test_io -j2: 1,047 run, 28 skipped, success.
  • Debug refleak python -m test -R 3:3 test_io.test_memoryio: success, no leaks in tracked runs.
  • git diff --check: success.
  • make patchcheck: success; one changed file.

@Taeknology
Taeknology marked this pull request as ready for review August 4, 2026 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve performance by using reference stealing methods

1 participant