Skip to content

Free-threaded dict.copy() spins on an already-held source lock when compacting a sparse dict #158525

Description

@joe-clickhouse

Bug report

On CPython 3.14.7 free-threaded, d.copy() becomes about 52 times slower than dict(d) after enough keys are deleted. This reproduces with one thread and no third-party modules.

Tested on macOS 26.7, arm64, using uv's Python build with the GIL disabled.

import statistics
import sys
import timeit

compact = dict.fromkeys(range(4), 13)
sparse = dict.fromkeys(range(10), 13)
for key in range(4, 10):
    del sparse[key]

assert compact == sparse
print(sys.version)
print("GIL enabled:", sys._is_gil_enabled())

for label, d, expression in [
    ("compact.copy()", compact, "d.copy()"),
    ("sparse.copy()", sparse, "d.copy()"),
    ("dict(sparse)", sparse, "dict(d)"),
]:
    samples = timeit.repeat(
        expression, globals={"d": d}, number=20000, repeat=5
    )
    print(label, statistics.median(samples) / 20000 * 1e6, "us/copy")

Measured median times:

Operation Microseconds per copy
compact.copy() 0.029
sparse.copy() 3.67
dict(sparse) 0.070

I expect copying a dict to avoid waiting on a lock the same thread already holds.

PyDict_Copy locks the source dict. When enough entries have been deleted, it calls dict_merge to make a compact copy. That function tries to lock both dicts, including the already-locked source. The thread then calls sched_yield() 40 times before parking releases the outer lock and allows progress. Lock code.

Related to #150902. That fix handles reacquiring the same pair of locks. This case holds one lock and then requests two.

CPython versions tested on:

3.14

Operating systems tested on:

macOS

Linked PRs

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions