GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — perf: pre-size dicts when the size is known by sobolevn · Pull Request #1243 · msgspec/msgspec

Conversation

@sobolevn

@sobolevn sobolevn commented Oct 11, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

When reading msgspec code I noticed that there are places that can benefit from using _PyDict_NewPresized. Here's how I designed this feature:

  1. I used a new function, which sets an upper cap for the size, so we don't allocate VERY big lists
  2. The cap is guessed
  3. We are using a non-public API, but it is stable enough to be used, public API will be release at some point in the future
  4. Dicts of 1–5 entries are unaffected (_PyDict_NewPresized falls back to PyDict_New there)

Most controvertial part: The one regression is JSON decoding of a sparse total=False TypedDict, where the dict is presized for all fields but few are present; msgpack and convert know the input size and are unaffected. I consider this to be a less common case. So, basically, we can: revert it back keep as-is, allowing more allocations on required fields.
Or we can keep this version. Which is my preffered option. Why?

  • It is rather uncommon to have lots of optional fields and only get a small subset of them (128 required and 4 present in my benchmark)
  • The absolute time increase is rather small, percents seem big, yes

If others do not agree on this trade-off, I can revert the json.decode change.

This benchmark is AI generated:

benchmark main this PR change
msgpack.decode, dict of 8 keys 0.184 µs 0.174 µs -5 %
msgpack.decode, dict of 1024 keys 51.5 µs 46.6 µs -10 %
msgpack.decode, dict of 100k keys (above the 1024 cap) 7.51 ms 7.47 ms -1 %
msgpack.decode, 250 dicts × 32 keys 432 µs 339 µs -22 %
msgpack.decode, TypedDict with 32 fields 0.641 µs 0.549 µs -14 %
json.decode, TypedDict with 32 fields 0.824 µs 0.740 µs -10 %
json.decode, total=False TypedDict with 32 fields, all present 0.822 µs 0.735 µs -11 %
json.decode, total=False TypedDict with 128 fields, 4 present 0.130 µs 0.182 µs +40 %
convert, dict of 1024 keys → dict[str, int] 21.9 µs 17.0 µs -22 %
convert, dict → TypedDict with 128 fields 3.24 µs 2.89 µs -11 %
to_builtins, dict of 1024 keys 23.6 µs 18.7 µs -21 %
to_builtins, dict of 1024 keys, order="sorted" 129 µs 114 µs -11 %
to_builtins, Struct with 32 fields 0.501 µs 0.475 µs -5 %
to_builtins, omit_defaults Struct with 32 fields, all omitted 0.083 µs 0.084 µs +1 %
to_builtins, dataclass with 32 fields 0.905 µs 0.781 µs -14 %
structs.asdict, Struct with 128 fields 1.56 µs 1.25 µs -20 %
__reduce__, kw-only Struct with 32 fields 0.453 µs 0.395 µs -13 %
msgpack.decode, benchmarks/ filesystem data (10 KiB) 23.3 µs 22.3 µs -4 %
to_builtins, benchmarks/ filesystem data (1 KiB) 1.56 µs 1.46 µs -7 %
control: json.decode, dict of 1024 keys (unchanged path) 58.3 µs 58.5 µs +1 %

bench.py

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
sobolevn added 3 commits October 11, 2026 09:25
@sobolevn
Use ms_presized_dict where it makes sense
2e7fb03
@sobolevn
Pre size dicts
1027cde
@sobolevn
Pre size dicts
319ae85
@sobolevn
sobolevn requested review from Siyet, ofek and provinzkraut October 11, 2026 08:44
sobolevn added 2 commits October 11, 2026 11:58
@sobolevn
Merge branch 'main' into ms_presized_dict
d243d32
@sobolevn
Revert json_decode change
1225991
@sobolevn

sobolevn commented Oct 11, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

Ok, on the other hand: I can open a new PR with json_decode part later.

Now json_decode change is reverted. I will create a new PR for that. So: we can merge this PR as a net-positive and decide on the corner-case in the next one.

Updated actual benchmarks:

benchmark main this PR change
msgpack.decode, dict of 8 keys 0.184 µs 0.174 µs -5 %
msgpack.decode, dict of 1024 keys 51.5 µs 46.6 µs -10 %
msgpack.decode, dict of 100k keys (above the 1024 cap) 7.51 ms 7.47 ms -1 %
msgpack.decode, 250 dicts × 32 keys 432 µs 339 µs -22 %
msgpack.decode, TypedDict with 32 fields 0.641 µs 0.549 µs -14 %
convert, dict of 1024 keys → dict[str, int] 21.9 µs 17.0 µs -22 %
convert, dict → TypedDict with 128 fields 3.24 µs 2.89 µs -11 %
to_builtins, dict of 1024 keys 23.6 µs 18.7 µs -21 %
to_builtins, dict of 1024 keys, order="sorted" 129 µs 114 µs -11 %
to_builtins, Struct with 32 fields 0.501 µs 0.475 µs -5 %
to_builtins, omit_defaults Struct with 32 fields, all omitted 0.083 µs 0.084 µs +1 %
to_builtins, dataclass with 32 fields 0.905 µs 0.781 µs -14 %
structs.asdict, Struct with 128 fields 1.56 µs 1.25 µs -20 %
__reduce__, kw-only Struct with 32 fields 0.453 µs 0.395 µs -13 %
msgpack.decode, benchmarks/ filesystem data (10 KiB) 23.3 µs 22.3 µs -4 %
to_builtins, benchmarks/ filesystem data (1 KiB) 1.56 µs 1.46 µs -7 %
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@provinzkraut

provinzkraut commented Oct 11, 2026

Copy link
Copy Markdown
Member

python/cpython#139772 mentions reservations about the usage of _PyDict_NewPresized and discourages its use. It also floats the idea of a deprecation. What are your thoughts on this? IMO this doesn't necessarily mean we cannot use it, but to me at least indicates we should be cautious.

Also, did you check the mentioned unicode-only key optimisation issue with _PyDict_NewPresized?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@sobolevn sobolevn mentioned this pull request Oct 11, 2026
[C API] Add PyDict_NewPresized() function python/cpython#139772
Closed
@sobolevn

sobolevn commented Oct 11, 2026

Copy link
Copy Markdown
Member Author

Oh, this is very sad to see: python/cpython#139772
I though that this issue will be in 3.16 and now it is closed :(

Yes, in this case - this change does not make sense. Because I thought that we can adapt msg_presized_dict to 3.16 and use the public API, but I've missed the May status update.

This is actually really not cool: we don't have any ways to limit the allocations for dict objects now. I will try to start this duscussion once again.

provinzkraut reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@sobolevn sobolevn closed this Oct 11, 2026
@provinzkraut

provinzkraut commented Oct 11, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Yeah, sad indeed, because the perf improvements did seem nice. IMO as long as it's not going to be deprecated any time soon and we don't have any other downsides (e.g. the mentioned UTF-8 optimisation), it'd be fine to use a private API. We do it in several places to get some perf benefits. Or do you think we should generally avoid private APIs unless absolutely necessary?

Seeing as the discussions around promoting such things to the public API usually involve referencing "x number of projects already use this private API, so there is a demand", it could be argued that using it is beneficial to drive interest in making it stable and public.

vstinner reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

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

Reviewers

@Siyet Siyet Awaiting requested review from Siyet
@ofek ofek Awaiting requested review from ofek
@provinzkraut provinzkraut Awaiting requested review from provinzkraut

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@sobolevn @provinzkraut
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.
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.