gh-150815: Speed up copy.deepcopy() of containers with atomic elements#150822
Open
gaborbernat wants to merge 1 commit into
Open
gh-150815: Speed up copy.deepcopy() of containers with atomic elements#150822gaborbernat wants to merge 1 commit into
gaborbernat wants to merge 1 commit into
Conversation
…lements The dict, list and tuple deep-copiers send every element back through deepcopy(), paying a function call even for atomic immutable elements that deepcopy() returns unchanged. Inline the atomic-type check into the three copiers so those elements are returned as-is. Behavior is identical, including shared references, recursion and int/tuple subclasses.
aeab6f7 to
8b8b7e8
Compare
|
|
||
|
|
||
| def _deepcopy_list(x, memo, deepcopy=deepcopy): | ||
| def _deepcopy_list(x, memo, deepcopy=deepcopy, _atomic=_atomic_types): |
Member
There was a problem hiding this comment.
Is this trick of capturing the global in a local still a net positive in newer Python?
Contributor
Author
There was a problem hiding this comment.
The benchmark numbers I provided were done against a build on the main branch. Now I haven't tried enabling the JIT or any other advanced features, but out of the box there is a significant benefit here.
Contributor
|
There are more options available to make deepcopy faster (see #91610 (comment)). If we want to make deepcopy faster, I believe we should gather enough support so a core dev can review a C (or rust?) implementation. With a C implementation we have much larger performance gains (see https://github.com/percolab/copium for example). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
copy.deepcopy()copies a structure by sending every element back throughdeepcopy(). For elements that need no copying at all — strings, ints,None, booleans, floats and the other immutable atomic types — that round trip costs a full function call each, even though the value handed back is the same object. Real data is dominated by these atomic leaves: a parsed JSON document, a settings dict cloned before mutation, a record copied inside a framework. The keys are strings and most values are strings and numbers, so copying spends most of its time callingdeepcopy()only to get the same object straight back.This folds the atomic-type check that already gates the top of
deepcopy()into thedict,listandtuplecopiers, so an atomic element is returned as-is without the per-item call. The check is the same onedeepcopy()runs, and atomic objects are not memoized either way, so the result is identical for shared references, recursive structures andint/tuplesubclasses.Deep-copying 105 JSON documents drawn from the top-1000 PyPI projects improves from 1.20 ms to 970 µs, 23% faster. This follows the atomic fast path added in gh-114264, extending it from the entry point to the per-element loop.
Benchmark (pyperf)
Run base vs patched by swapping
Lib/copy.pyon the same interpreter. The figure above is from 105 JSON documents in the top-1000 PyPI corpus; the self-contained script below builds an equivalent atomic-heavy structure and shows a comparable percentage gain.Resolves #150815.