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
{{ message }}
Repository navigation
Optimize PyUnicode_FromString for short ASCII strings #81529
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=<Date2019-06-24.03:30:43.577>created_at=<Date2019-06-20.11:40:57.663>labels= ['interpreter-core', '3.9', 'performance']
title='Optimize PyUnicode_FromString for short ASCII strings'updated_at=<Date2019-06-24.07:08:11.005>user='https://github.com/methane'
_PyUnicode_FromASCII(s, len) is faster than PyUnicode_FromString(s) because PyUnicode_FromString() uses temporary _PyUnicodeWriter to support UTF-8.
I don't understand how _PyUnicodeWriter could be slow. It does not overallocate by default. It's just wrapper to implement efficient memory management.
Oh, wait. Why we used _PyUnicodeWriter here?
To optimize decoding errors: the error handler can use replacement string longer than 1 character. Overallocation is used in this case.
I don't understand how _PyUnicodeWriter could be slow. It does not overallocate by default. It's just wrapper to implement efficient memory management.
I misunderstood _PyUnicodeWriter. I thought it caused one more allocation, but it doesn't.
But _PyUnicodeWriter is still slow, because gcc and clang are not smart enough to optimize _PyUnicodeWriter_Init() & _PyUnicodeWriter_Prepare().
PyUnicode_FromString() takes about 4 sec on my machine. _PyUnicode_FromASCII() is about 2 sec.
By skipping _PyUnicodeWriter for ASCII string (GH-14283), PyUnicode_FromString() takes about 3 sec.
$ time ./x # PyUnicode_FromString
real 0m4.085s
user 0m4.081s
sys 0m0.004s
$ time ./y # PyUnicode_FromString (skip _PyUnicode_Writer, python/cpython#58491)
real 0m2.988s
user 0m2.988s
sys 0m0.000s
$ time ./z # _PyUnicode_FromASCII
$ time ./z
real 0m1.975s
user 0m1.975s
sys 0m0.000s
changed the title [-]Optimize PyUnicode_GetString for short ASCII strings[/-][+]Optimize PyUnicode_FromString for short ASCII strings[/+]on Jun 21, 2019
Could you please measure the performance for long strings (1000, 10000 and 100000 characters): a long ASCII string and a long ASCII string ending with a non-ASCII character?
Activity
methane commentedon Jun 20, 2019
_PyUnicode_FromASCII(s, len) is faster than PyUnicode_FromString(s) because PyUnicode_FromString() uses temporary _PyUnicodeWriter to support UTF-8.
But _PyUnicode_FromASCII("hello", strlen("hello"))
is not as easy asPyUnicode_FromString("hello")`._PyUnicode_FROM_ASCII() is simple macro which wraps _PyUnicode_FromASCII which calls strlen() automatically:
I believe recent compilers optimize away calls of strlen().
vstinner commentedon Jun 20, 2019
LGTM.
methane commentedon Jun 20, 2019
I confirmed at least one measurable speedup:
./python -m pyperf timeit -s 'd={}' -- 'repr(d)'
FromString: Mean +- std dev: 157 ns +- 3 ns
FROM_ASCII: Mean +- std dev: 132 ns +- 3 ns
methane commentedon Jun 20, 2019
Should we make these APIs public?
_PyUnicode_FromASCIItoPyUnicode_FromASCIIAndSize.PyUnicode_FromASCIIas static inline function (without ABI).#define _PyUnicode_FromASCII PyUnicode_FromASCIIAndSizefor source code compatibility.serhiy-storchaka commentedon Jun 20, 2019
Most of changes are in not performance sensitive code. I do not think there is a benefit of using new macro there.
If PyUnicode_FromString() is slow I prefer to optimize it instead of adding yet one esoteric private function for internal needs.
In case of dict.__repr__() we can get even more gain by using _Py_IDENTIFIER or more general API proposed by Victor.
methane commentedon Jun 20, 2019
Because I used just sed.
OK. There are some code like
PyUnicode_FromString(name). Optimizing PyUnicode_FromString will be more effective. I created PR 14273.Of course, I used it just for micro benchmarking. Optimizing it is not a goal. In case of PR 14273:
[-]add _PyUnicode_FROM_ASCII macro[/-][+]Optimize PyUnicode_GetString for short ASCII strings[/+]methane commentedon Jun 20, 2019
Oh, wait. Why we used _PyUnicodeWriter here?
Decoding UTF-8 must not require it. 2-pass is enough.
vstinner commentedon Jun 20, 2019
I don't understand how _PyUnicodeWriter could be slow. It does not overallocate by default. It's just wrapper to implement efficient memory management.
To optimize decoding errors: the error handler can use replacement string longer than 1 character. Overallocation is used in this case.
methane commentedon Jun 21, 2019
I misunderstood _PyUnicodeWriter. I thought it caused one more allocation, but it doesn't.
But _PyUnicodeWriter is still slow, because gcc and clang are not smart enough to optimize _PyUnicodeWriter_Init() & _PyUnicodeWriter_Prepare().
See this example:
PyUnicode_FromString() takes about 4 sec on my machine. _PyUnicode_FromASCII() is about 2 sec.
By skipping _PyUnicodeWriter for ASCII string (GH-14283), PyUnicode_FromString() takes about 3 sec.
methane commentedon Jun 21, 2019
Another micro benchmark:
methane commentedon Jun 21, 2019
PR 14291 seems simpler than PR 14283. But PR 14283 is faster because _PyUnicodeWriter is a learge struct.
master: 3.7sec
PR 14283: 2.9sec
PR 14291: 3.45sec
compiler: gcc (Ubuntu 8.3.0-6ubuntu1) 8.3.0
without LTO, without PGO
vstinner commentedon Jun 21, 2019
I'm confused by the issue title: PyUnicode_GetString() doesn't exist, it's PyUnicode_FromString() :-) I changed the title.
[-]Optimize PyUnicode_GetString for short ASCII strings[/-][+]Optimize PyUnicode_FromString for short ASCII strings[/+]methane commentedon Jun 24, 2019
New changeset 770847a by Inada Naoki in branch 'master':
bpo-37348: optimize decoding ASCII string (GH-14283)
770847a
serhiy-storchaka commentedon Jun 24, 2019
Could you please measure the performance for long strings (1000, 10000 and 100000 characters): a long ASCII string and a long ASCII string ending with a non-ASCII character?
methane commentedon Jun 24, 2019
This optimization is only for short strings. There is no significant difference for long and non-ASCII strings.