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 exact str 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 commented on 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 commented on 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 commented on Oct 10, 2025
methane commented on 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. 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.
>>> j = '{' + ','.join(['"foo":1']*1000)+ '}' >>> import orjson >>> import sys >>> d=orjson.loads(j) >>> len(d) 1 >>> sys.getsizeof(d) 36952The 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 commented on 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 commented on Oct 10, 2025
I created an issue to Cython.
cython/cython#7201
methane commented on 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 to dict_new_presized()?
_PyDict_NewPresized(minsize) would be deprecated.
scoder commented on 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 commented on Oct 12, 2025
Ok, I updated #139773 to add unicode_keys parameter.
scoder commented on 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 commented on 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 have PyDict_NewPresized(size, unicode_keys) too?
scoder commented on 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 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.
encukou commented on Oct 20, 2025
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 exact str 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.
pythongh-139772: Add PyDict_FromItems() function
pythongh-139772: Add PyDict_FromItems() function
cext: remove `_dict_new_presized` util
vstinner commented on May 29, 2026
After months of exploring different PyDict APIs, I decided to give up on this optimization: see capi-workgroup/decisions#90 (comment).
sobolevn commented on Oct 11, 2026
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.
See msgspec/msgspec#1243