Float encoding is a particularly weak area for the json library, especially for large-magnitude floats. Would it be possible to optimize this by adding a special case to PyUnicodeWriter_WriteRepr(), similar to _PyLong_FormatWriter() for integers?
A fast path in PyUnicodeWriter_WriteRepr seems reasonable, but would not help a lot (I did a quick benchmark, only about 10% depending on the type of floats). A faster float-to-str would help more. There are algorithms for it, but not readily available in C. See https://discuss.python.org/t/faster-float-to-string-conversions/107397
@vstinner and I discussed this at the sprint that we are going to measure general benchmark how much this call paths are executed and adding the fast path.
I added the int fast-path in #125214 which contains benchmark results: it was between 1.54x and 1.93x faster on repr(list) depending on the list size (between 3 and 50 items).
FYI I added the str fast-path in #133969 which also contains benchmark results (1.18x faster in average, up to 1.61x faster in the best case).
In the past, I considered to add a type slot (like PyTypeObject.tp_str_writer and/or PyTypeObject.tp_repr_writer) instead of using hardcoded fast paths. If we add multiple fast paths in the future, we might reconsider adding a type slot?
pyperformance would not be a good benchmark for adding this fast path: 0% of calls.
But even in the standard library test suite:
Argument type
Calls
Share
Note
exact int
2,924,088
86.7%
existing fast path (_PyLong_FormatWriter)
None
181,066
5.4%
exact list
69,501
2.1%
other types
58,946
1.7%
user classes, type objects, etc.
exact str
58,158
1.7%
exact dict
40,537
1.2%
bool
20,759
0.6%
exact float
10,657
0.32%
proposed fast path
exact bytes
5,700
0.2%
exact tuple
2,244
0.07%
total
3,371,663
100%
So I am -1 unless we can provide a benchmark that justifies this fast path, and even then float would not be the first candidate, since it is lower than str and dict.
PyUnicode_Format() calls formatfloat() for %f (and other similar formats): it calls PyOS_double_to_string() + _PyUnicodeWriter_WriteASCIIString().
float.__float__() calls _PyFloat_FormatAdvancedWriter() which is implemented with PyOS_double_to_string() + _PyUnicodeWriter_WriteASCIIString() for the common case.
but I used git push --force and so lost my work :-(
unless you have very aggressive settings for cleaning up unreachable commits locally, you should be able to recover it by inspecting
git reflog --date=iso
and scrolling/paging down to around the time when those commits were made. If you find the hash of a commit from that work (ideally the last one of course), you can then check out a new branch from this.
Activity
eendebakpt commented on Oct 19, 2026
A fast path in PyUnicodeWriter_WriteRepr seems reasonable, but would not help a lot (I did a quick benchmark, only about 10% depending on the type of floats). A faster float-to-str would help more. There are algorithms for it, but not readily available in C. See https://discuss.python.org/t/faster-float-to-string-conversions/107397
corona10 commented on Oct 19, 2026
@eendebakpt @nineteendo
@vstinner and I discussed this at the sprint that we are going to measure general benchmark how much this call paths are executed and adding the fast path.
vstinner commented on Oct 19, 2026
I added the int fast-path in #125214 which contains benchmark results: it was between 1.54x and 1.93x faster on repr(list) depending on the list size (between 3 and 50 items).
FYI I added the str fast-path in #133969 which also contains benchmark results (1.18x faster in average, up to 1.61x faster in the best case).
vstinner commented on Oct 19, 2026
In the past, I considered to add a type slot (like PyTypeObject.tp_str_writer and/or PyTypeObject.tp_repr_writer) instead of using hardcoded fast paths. If we add multiple fast paths in the future, we might reconsider adding a type slot?
corona10 commented on Oct 19, 2026
So I am -1 unless we can provide a benchmark that justifies this fast path, and even then float would not be the first candidate, since it is lower than str and dict.
vstinner commented on Oct 20, 2026
If floats are more common in json than in general in Python, json can add a fast-path for float using the internal C API.
The _json extension already has a code path for float which gets a PyUnicodeWriter:
vstinner commented on Oct 20, 2026
PyUnicode_Format() calls formatfloat() for %f (and other similar formats): it calls PyOS_double_to_string() + _PyUnicodeWriter_WriteASCIIString().
float.__float__() calls _PyFloat_FormatAdvancedWriter() which is implemented with PyOS_double_to_string() + _PyUnicodeWriter_WriteASCIIString() for the common case.
PyUnicode_FromFormat() doesn't support %f format.
pythongh-159702: Add fast-path for float in _json encoder
vstinner commented on Oct 20, 2026
I wrote PR gh-159826 to do that. It calls PyOS_double_to_string() + PyUnicodeWriter_WriteASCII() (functions of the public C API).
vstinner commented on Oct 20, 2026
@nineteendo: Would it be possible for you to re-run your benchmark on my PR?
Oh crap, I wrote #156897 (JSON float optimization for json.loads()) last month, but I used git push --force and so lost my work :-(
See also issue gh-150638.
h-vetinari commented on Oct 21, 2026
unless you have very aggressive settings for cleaning up unreachable commits locally, you should be able to recover it by inspecting
and scrolling/paging down to around the time when those commits were made. If you find the hash of a commit from that work (ideally the last one of course), you can then check out a new branch from this.