Skip to content

gh-161850: Copy a deque into a list under a single critical section - #161854

Draft
113xiaoji wants to merge 5 commits into
python:mainfrom
113xiaoji:deque-bulk-list-extend
Draft

113xiaoji wants to merge 5 commits into
python:mainfrom
113xiaoji:deque-bulk-list-extend

Conversation

@113xiaoji

@113xiaoji 113xiaoji commented Nov 12, 2026 •

Copy link
Copy Markdown

Summary

Depends on #161853. This is a draft follow-up. The branch currently contains the commits from #161853, so the diff against main includes that prerequisite. The PR2-only changes are deque/list bulk materialization and its tests/NEWS entry.

Closes #161850 once both changes are merged.

Changes

  • Add a fast path for copying a deque or the remaining items of a forward deque iterator into a list while holding the list and deque critical sections once.
  • Register the internal fast-path hooks from _collections, where the deque types are defined. Store the hook pointer in per-interpreter state, using release/acquire publication for concurrent free-threaded access.
  • Recognize the supported built-in slots and fall back for subclasses overriding iteration or length behavior.
  • Preserve iterator mutation detection, exhaustion/reduction state and generic-path list allocation behavior in the covered cases.
  • Add block-boundary, partially consumed iterator, subclass, allocation/refcount and concurrent copy/iteration tests.

The atomic bulk copy can change which outcomes are observed under concurrent mutation; this is not a claim that it reproduces every possible interleaving of repeated next() calls. The supported built-in-slot paths avoid user-defined iteration/length callbacks.

Test Plan

  • Source review and git diff --check pass for the PR2-only delta and the combined branch.
  • Earlier byte-for-byte Mako HTML/API checks and performance measurements used a CPython 3.14.6 backport, not this main-based (3.16 development) port.
  • Main-based pydebug builds of ordinary and free-threaded CPython pass test_deque, test_list, test_collections, test_free_threading.test_collections, test_embed and test_interpreters (the GIL-specific suites are skipped in the opposite configuration).
  • The new-head generated-files job passes the full C globals check, exported symbols, limited ABI and regeneration checks. The complete CI matrix passed all 48 jobs on head b83e4241979bfbf5036008b0b3675ac50ca884c5, including ordinary/free-threaded TSan, ASan, MSan, UBSan, fuzz and platform tests.
  • This PR stays draft while the prerequisite and hook design are reviewed.

For context, the combined 3.14.6 backport reduced unmodified Mako's FT render time by approximately 33% on Kunpeng 950 and 9% on EPYC 9654 in the issue's measurements. These are single-thread workload results and do not isolate this PR from its prerequisite.

AI tools assisted preparation of the change and submission text.

113xiaoji and others added 4 commits November 12, 2026 14:52
In the free-threaded build, dequeiter_next(), dequereviter_next() and
dequeiter_reduce() entered a two-object critical section on the iterator
and its deque.  All writes to the iterator's state already happen while
holding the deque's lock, so the deque's critical section alone is enough
to serialize concurrent next() calls on a shared iterator.  This halves
the atomic read-modify-write operations per item.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
list(d), [*d], list.extend(d) and PySequence_Fast(d) -- and hence
str.join(d), which hands the deque's iterator to list.extend() --
fetched one item per tp_iternext call, each entering a critical section.
Add a fast path that copies all remaining items of a deque or a forward
deque iterator while holding the list's and the deque's critical sections
once.  The _collections module registers it with listobject.c, since the
deque type lives in that module.  The result is identical to running the
iterator to exhaustion: no user code runs, the mutation check matches
next(), and the list's allocation matches the generic path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@python-cla-bot

python-cla-bot Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

Keep the mutable deque hook pointer in PyInterpreterState instead of
 a C global so check-c-globals accepts it. Preserve release/acquire
publication for concurrent free-threaded registration.

Validated with ordinary and free-threaded pydebug builds, the focused
C globals check, and deque/list/collections/embed/interpreter tests.

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

Labels

type-feature A feature request or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Free-threaded build: deque iteration and list(deque) take a critical section per item

1 participant