You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I don't want to make this API public. Most users either overestimate its effectiveness or don't fully understand how it operates.
I will check how this API is used in the modules you listed.
As a C programmer, it's natural to want to use a presized constructor to avoid reallocations. But preallocating a dict is not as straightforward as for a list.
Minsize issue
Dict minsize is 8 (5 entries). For size <= 5, _PyDict_NewPresized() calls _PyDict_New(). It is just an overhead.
gevent and mypy has this issue.
Unicode issue
Current dict implementation has optimization for all keys are unicode. But _PyDict_NewPresized() doesn't provide information about all keys are unicode or not.
On the other hand, PyDict_New() doesn't allocate hashtable. First PyDict_SetItem() allocates minsize keys table with information the first key is unicode or not.
This is the main reason why CPython itself doesn't use _PyDict_NewPresized().
CPython uses _PyDict_FromItems() instead.
Most of libraries listed above have this issue.
Distinct-keys issue
Using _PyDict_NewPresized() without counting distinct-keys would waste much RAM.
msgpack includes the size in the container header for preallocation.
But by crafting a false size header, you can request the preallocation of a dict with 100 million elements using just 10 bytes of msgpack data.
This is a problem for lists as well, but it becomes more serious for dicts because they have a larger memory overhead per element.
That's why in msgpack-python, which I maintain, we don't use _PyDict_NewPresized().
Preallocation is really tempting. Some users loving microbenchmarks want it even though there are no guarantee about keys uniqueness.
How about adding PyDict_NewPresized(Py_ssize_t expected_size, int unicode_keys) that is same to dict_new_presized()? _PyDict_NewPresized(minsize) would be deprecated.
I'd be happy to have a PyDict_FromItems(PyObject *const *keys, PyObject *const *values, Py_ssize_t length) instead that creates a dict from C arrays of keys and values.
There's _PyDict_FromItems() which allows defining steps for the keys and values arrays, but I doubt that that'd be commonly useful as a public API. The simpler form seems to cover the main use cases.
To be sure that it matches Python's semantics, I just tested the following in Py3.12:
How about adding PyDict_NewPresized(Py_ssize_t expected_size, int unicode_keys) that is same to dict_new_presized()?
_PyDict_NewPresized(minsize) would be deprecated.
Ok, I updated #139773 to add unicode_keys parameter.
High-level APIs are only useful when they match typical use cases.
In CPython, _PyDict_FromItems() is used when building map from stack.
How third parties use _PyDict_FromItems() if we have PyDict_NewPresized(size, unicode_keys) too?
How third parties use _PyDict_FromItems() if we have PyDict_NewPresized(size, unicode_keys) too?
So, I looked through a bit of code and with vectorcalls taking many keyword dicts away, constant sized dicts have become a lot less important these days, apart from literals. The "FromItems" function is really mostly for convenience (thus, high-level) and therefore not required. It does have the advantage, though, that the Python runtime can see the full set of keys and values upfront and might decide to use a specifically adapted dict layout or implementation. PyPy might benefit from that, for example, as might other Python implementations.
If the intention is to keep the API small, then PyDict_NewPresized() with the additional unicode_keys option would cover the immediate need. Type knowledge about the keys will often be available on user side, so being able to pass that in seems the right approach. A question is then whether this will remain the only relevant configuration option and whether "unicode keys or not" will remain relevant for future dict implementations. I don't know. Maybe. But the dict implementation has already gone through several iterations and I could imagine future specialisations for other types of keys, e.g. integers. C doesn't easily allow extending the interface here.
What do you think of allowing the Py_TPFLAGS_XXX_SUBCLASS flags as option values to specify a known homogeneous key type? They could even be |-ed if the keys are a mix of more than one type, in case the runtime can ever make use of that.
Using Py_TPFLAGS wouldn't be future-friendly: we can't add new flags to that field any more.
Also, I don't think we want to allow subclasses -- the optimizations I can imagine will want to erase the Python type, so they'll need exactstr or exact small int.
I guess that argument should be a type object -- you can't | those together, but it would make for more regular API.
And I think it should be a hint, not a promise (unless that hurts performance). Putting in a subclass instance should be fine (though perhaps slow), so we'd need type checks anyway.
In msgspec we get two-digit percents speed-up from using _PyDict_NewPresized() API.
I was expecting that new PyDict_NewPresized(size, unicode) API would be available soon :(
But, instead I found that this issue is now closed.
Activity
pythongh-139772: Add PyDict_NewPresized() function
pythongh-139772: Add PyDict_NewPresized() function
pythongh-139772: Add PyDict_NewPresized() function
methane commentedon Oct 9, 2025
I don't want to make this API public. Most users either overestimate its effectiveness or don't fully understand how it operates.
I will check how this API is used in the modules you listed.
vstinner commentedon Oct 9, 2025
Do you mean that it's not efficient to preallocate enough entries in the dictionary?
The strange thing about
_PyDict_NewPresized()is that it's not used in Python.Should we deprecate the function instead, and guide users towards
PyDict_New()instead?methane commentedon Oct 10, 2025
methane commentedon Oct 10, 2025
As a C programmer, it's natural to want to use a presized constructor to avoid reallocations. But preallocating a dict is not as straightforward as for a list.
Minsize issue
Dict minsize is 8 (5 entries). For size <= 5,
_PyDict_NewPresized()calls_PyDict_New(). It is just an overhead.gevent and mypy has this issue.
Unicode issue
Current dict implementation has optimization for all keys are unicode. But
_PyDict_NewPresized()doesn't provide information about all keys are unicode or not.On the other hand,
PyDict_New()doesn't allocate hashtable. FirstPyDict_SetItem()allocates minsize keys table with information the first key is unicode or not.This is the main reason why CPython itself doesn't use
_PyDict_NewPresized().CPython uses
_PyDict_FromItems()instead.Most of libraries listed above have this issue.
Distinct-keys issue
Using
_PyDict_NewPresized()without counting distinct-keys would waste much RAM.The false size header problem
msgpack includes the size in the container header for preallocation.
But by crafting a false size header, you can request the preallocation of a dict with 100 million elements using just 10 bytes of msgpack data.
This is a problem for lists as well, but it becomes more serious for dicts because they have a larger memory overhead per element.
That's why in msgpack-python, which I maintain, we don't use
_PyDict_NewPresized().methane commentedon Oct 10, 2025
I created an issue to mercurial.
https://foss.heptapod.net/mercurial/mercurial-devel/-/issues/10029
Despite its over-allocation, it seems mercurial doesn't have unicode-issue because they use bytes for paths.
methane commentedon Oct 10, 2025
I created an issue to Cython.
cython/cython#7201
methane commentedon Oct 11, 2025
Preallocation is really tempting. Some users loving microbenchmarks want it even though there are no guarantee about keys uniqueness.
How about adding
PyDict_NewPresized(Py_ssize_t expected_size, int unicode_keys)that is same todict_new_presized()?_PyDict_NewPresized(minsize)would be deprecated.scoder commentedon Oct 11, 2025
I'd be happy to have a
PyDict_FromItems(PyObject *const *keys, PyObject *const *values, Py_ssize_t length)instead that creates a dict from C arrays of keys and values.There's
_PyDict_FromItems()which allows defining steps for the keys and values arrays, but I doubt that that'd be commonly useful as a public API. The simpler form seems to cover the main use cases.To be sure that it matches Python's semantics, I just tested the following in Py3.12:
So it's "first create all, then insert all", as the proposed C-API function would implement.
pythongh-139772: Add PyDict_FromItems() function
2 remaining items
pythongh-139772: Add PyDict_NewPresized() function
pythongh-139772: Add PyDict_NewPresized() function
pythongh-139772: Add PyDict_NewPresized() function
pythongh-139772: Add PyDict_NewPresized() function
vstinner commentedon Oct 12, 2025
Ok, I updated #139773 to add unicode_keys parameter.
scoder commentedon Oct 17, 2025
I think the two APIs serve different use cases that are both valid. The first is more low-level, the other more high-level (from a C point of view).
methane commentedon Oct 18, 2025
High-level APIs are only useful when they match typical use cases.
In CPython,
_PyDict_FromItems()is used when building map from stack.How third parties use
_PyDict_FromItems()if we havePyDict_NewPresized(size, unicode_keys)too?scoder commentedon Oct 19, 2025
So, I looked through a bit of code and with vectorcalls taking many keyword dicts away, constant sized dicts have become a lot less important these days, apart from literals. The "FromItems" function is really mostly for convenience (thus, high-level) and therefore not required. It does have the advantage, though, that the Python runtime can see the full set of keys and values upfront and might decide to use a specifically adapted dict layout or implementation. PyPy might benefit from that, for example, as might other Python implementations.
If the intention is to keep the API small, then
PyDict_NewPresized()with the additionalunicode_keysoption would cover the immediate need. Type knowledge about the keys will often be available on user side, so being able to pass that in seems the right approach. A question is then whether this will remain the only relevant configuration option and whether "unicode keys or not" will remain relevant for future dict implementations. I don't know. Maybe. But the dict implementation has already gone through several iterations and I could imagine future specialisations for other types of keys, e.g. integers. C doesn't easily allow extending the interface here.What do you think of allowing the
Py_TPFLAGS_XXX_SUBCLASSflags as option values to specify a known homogeneous key type? They could even be|-ed if the keys are a mix of more than one type, in case the runtime can ever make use of that.encukou commentedon Oct 20, 2025
Using
Py_TPFLAGSwouldn't be future-friendly: we can't add new flags to that field any more.Also, I don't think we want to allow subclasses -- the optimizations I can imagine will want to erase the Python type, so they'll need exact
stror exact smallint.I guess that argument should be a type object -- you can't
|those together, but it would make for more regular API.And I think it should be a hint, not a promise (unless that hurts performance). Putting in a subclass instance should be fine (though perhaps slow), so we'd need type checks anyway.
pythongh-139772: Add PyDict_FromItems() function
pythongh-139772: Add PyDict_FromItems() function
cext: remove `_dict_new_presized` util
vstinner commentedon May 29, 2026
After months of exploring different
PyDictAPIs, I decided to give up on this optimization: see capi-workgroup/decisions#90 (comment).sobolevn commentedon Oct 11, 2026
In
msgspecwe get two-digit percents speed-up from using_PyDict_NewPresized()API.I was expecting that new
PyDict_NewPresized(size, unicode)API would be available soon :(But, instead I found that this issue is now closed.
See msgspec/msgspec#1243