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
Attached patch optimizes the UTF-8 encoder for error handlers: ignore, replace, surrogateescape, surrogatepass. It is based on the patch faster_surrogates_hadling.patch written by Serhiy Storchaka in the issue bpo-24870.
It also modifies unicode_encode_ucs1() to use memset() for the replace error handler. It should be faster for long sequences of unencodable characters, but it may be slower for short sequences of unencodable characters.
The patch adds new unit tests and fix unit tests to ensure that utf-8-sig codec is also well tested.
TODO: write a benchmark.
See also the issue bpo-25227 which optimized ASCII and latin1 encoders with the surrogateescape error handlers.
Platform of campaign before:
Date: 2015-10-01 13:30:07
Timer precision: 61 ns
Python version: 3.6.0a0 (default:10efb1797e7b, Oct 1 2015, 13:30:06) [GCC 5.1.1 20150618 (Red Hat 5.1.1-4)]
Platform of campaign after:
Timer precision: 63 ns
Date: 2015-10-01 13:54:14
Python version: 3.6.0a0 (default:10efb1797e7b+, Oct 1 2015, 13:53:51) [GCC 5.1.1 20150618 (Red Hat 5.1.1-4)]
--------------------------+-------------+----------------
ignore: "\udcff" * length | before | after
--------------------------+-------------+----------------
Total | 2.66 ms (*) | 8.19 us (-100%)
--------------------------+-------------+----------------
--------------------------------+-------------+---------------
ignore: "a" * length + "\udcff" | before | after
--------------------------------+-------------+---------------
Total | 263 ms (*) | 930 us (-100%)
---------------------------------------+-------------+----------------
--------------------------------+-------------+---------------
ignore: "\udcff" + "a" * length | before | after
--------------------------------+-------------+---------------
Total | 14.9 us (*) | 14.6 us
--------------------------------+-------------+---------------
---------------------------+-------------+----------------
replace: "\udcff" * length | before | after
---------------------------+-------------+----------------
Total | 2.94 ms (*) | 8.52 us (-100%)
---------------------------+-------------+----------------
---------------------------------+-------------+---------------
replace: "a" * length + "\udcff" | before | after
---------------------------------+-------------+---------------
Total | 290 ms (*) | 1.02 ms (-100%)
----------------------------------------+-------------+----------------
---------------------------------+-------------+---------------
replace: "\udcff" + "a" * length | before | after
---------------------------------+-------------+---------------
Total | 15.1 us (*) | 14.7 us
---------------------------------+-------------+---------------
-----------------------------------+-------------+---------------
surrogateescape: "\udcff" * length | before | after
-----------------------------------+-------------+---------------
Total | 2.97 ms (*) | 16.3 us (-99%)
-----------------------------------+-------------+---------------
-----------------------------------------+-------------+---------------
surrogateescape: "a" * length + "\udcff" | before | after
-----------------------------------------+-------------+---------------
Total | 292 ms (*) | 1.93 ms (-99%)
------------------------------------------------+-------------+---------------
-----------------------------------------+-------------+---------------
surrogateescape: "\udcff" + "a" * length | before | after
-----------------------------------------+-------------+---------------
Total | 15.1 us (*) | 14.6 us
-----------------------------------------+-------------+---------------
---------------------------------+-------------+---------------
surrogatepass: "\udcff" * length | before | after
---------------------------------+-------------+---------------
Total | 3.2 ms (*) | 25.8 us (-99%)
---------------------------------+-------------+---------------
---------------------------------------+-------------+---------------
surrogatepass: "a" * length + "\udcff" | before | after
---------------------------------------+-------------+---------------
Total | 316 ms (*) | 2.8 ms (-99%)
----------------------------------------------+-------------+---------------
---------------------------------------+-------------+---------------
surrogatepass: "\udcff" + "a" * length | before | after
---------------------------------------+-------------+---------------
Total | 15.2 us (*) | 14.4 us (-5%)
---------------------------------------+-------------+---------------
------------------------------------+-------------+---------------
backslashreplace: "\udcff" * length | before | after
------------------------------------+-------------+---------------
Total | 3.52 ms (*) | 112 us (-97%)
------------------------------------+-------------+---------------
------------------------------------------+-------------+---------------
backslashreplace: "a" * length + "\udcff" | before | after
------------------------------------------+-------------+---------------
Total | 347 ms (*) | 15.3 ms (-96%)
-------------------------------------------------+-------------+---------------
------------------------------------------+-------------+---------------
backslashreplace: "\udcff" + "a" * length | before | after
------------------------------------------+-------------+---------------
Oh, the default handler for errror handlers uses a loop to check for non-ASCII characters. It can be replaced with PyUnicode_IS_ASCII(str) which has a complexity O(1). Done in new patch.
Activity
vstinner commentedon Sep 29, 2015
Attached patch optimizes the UTF-8 encoder for error handlers: ignore, replace, surrogateescape, surrogatepass. It is based on the patch faster_surrogates_hadling.patch written by Serhiy Storchaka in the issue bpo-24870.
It also modifies unicode_encode_ucs1() to use memset() for the replace error handler. It should be faster for long sequences of unencodable characters, but it may be slower for short sequences of unencodable characters.
The patch adds new unit tests and fix unit tests to ensure that utf-8-sig codec is also well tested.
TODO: write a benchmark.
See also the issue bpo-25227 which optimized ASCII and latin1 encoders with the surrogateescape error handlers.
vstinner commentedon Oct 1, 2015
Oh, there is a bug in utf8_encoder() (not in my patch!), newpos was not used after calling the error handler. It's now fixed in the new patch.
vstinner commentedon Oct 1, 2015
Benchmark results. Sorry for the very long output.
There are some (corner?) cases where the patched Python is a little bit slower. I consider that it's ok since it's *much* faster in the other cases.
What do you think?
Common platform:
Timer info: namespace(adjustable=False, implementation='clock_gettime(CLOCK_MONOTONIC)', monotonic=True, resolution=1e-09)
Bits: int=32, long=64, long long=64, size_t=64, void*=64
CPU model: Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz
Python unicode implementation: PEP-393
Timer: time.perf_counter
SCM: hg revision=10efb1797e7b+ tag=tip branch=default date="2015-10-01 13:16 +0200"
Platform: Linux-4.1.6-200.fc22.x86_64-x86_64-with-fedora-22-Twenty_Two
CFLAGS: -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
Platform of campaign before:
Date: 2015-10-01 13:30:07
Timer precision: 61 ns
Python version: 3.6.0a0 (default:10efb1797e7b, Oct 1 2015, 13:30:06) [GCC 5.1.1 20150618 (Red Hat 5.1.1-4)]
Platform of campaign after:
Timer precision: 63 ns
Date: 2015-10-01 13:54:14
Python version: 3.6.0a0 (default:10efb1797e7b+, Oct 1 2015, 13:53:51) [GCC 5.1.1 20150618 (Red Hat 5.1.1-4)]
--------------------------+-------------+----------------
ignore: "\udcff" * length | before | after
--------------------------+-------------+----------------
Total | 2.66 ms (*) | 8.19 us (-100%)
--------------------------+-------------+----------------
--------------------------------+-------------+---------------
ignore: "a" * length + "\udcff" | before | after
--------------------------------+-------------+---------------
Total | 15 us (*) | 14.6 us
--------------------------------+-------------+---------------
--------------------------------------------+-------------+---------------
ignore: ("a" * 99 + "\udcff" * 99) * length | before | after
--------------------------------------------+-------------+---------------
Total | 265 ms (*) | 2.82 ms (-99%)
--------------------------------------------+-------------+---------------
---------------------------------------+-------------+----------------
ignore: ("\udcff" * 99 + "a") * length | before | after
---------------------------------------+-------------+----------------
Total | 263 ms (*) | 930 us (-100%)
---------------------------------------+-------------+----------------
--------------------------------+-------------+---------------
ignore: "\udcff" + "a" * length | before | after
--------------------------------+-------------+---------------
Total | 14.9 us (*) | 14.6 us
--------------------------------+-------------+---------------
---------------------------+-------------+----------------
replace: "\udcff" * length | before | after
---------------------------+-------------+----------------
Total | 2.94 ms (*) | 8.52 us (-100%)
---------------------------+-------------+----------------
---------------------------------+-------------+---------------
replace: "a" * length + "\udcff" | before | after
---------------------------------+-------------+---------------
Total | 18 us (*) | 14.7 us (-18%)
---------------------------------+-------------+---------------
---------------------------------------------+-------------+---------------
replace: ("a" * 99 + "\udcff" * 99) * length | before | after
---------------------------------------------+-------------+---------------
Total | 293 ms (*) | 3.21 ms (-99%)
---------------------------------------------+-------------+---------------
----------------------------------------+-------------+----------------
replace: ("\udcff" * 99 + "a") * length | before | after
----------------------------------------+-------------+----------------
Total | 290 ms (*) | 1.02 ms (-100%)
----------------------------------------+-------------+----------------
---------------------------------+-------------+---------------
replace: "\udcff" + "a" * length | before | after
---------------------------------+-------------+---------------
Total | 15.1 us (*) | 14.7 us
---------------------------------+-------------+---------------
-----------------------------------+-------------+---------------
surrogateescape: "\udcff" * length | before | after
-----------------------------------+-------------+---------------
Total | 2.97 ms (*) | 16.3 us (-99%)
-----------------------------------+-------------+---------------
-----------------------------------------+-------------+---------------
surrogateescape: "a" * length + "\udcff" | before | after
-----------------------------------------+-------------+---------------
Total | 15.3 us (*) | 14.6 us
-----------------------------------------+-------------+---------------
-----------------------------------------------------+-------------+---------------
surrogateescape: ("a" * 99 + "\udcff" * 99) * length | before | after
-----------------------------------------------------+-------------+---------------
Total | 295 ms (*) | 4.04 ms (-99%)
-----------------------------------------------------+-------------+---------------
------------------------------------------------+-------------+---------------
surrogateescape: ("\udcff" * 99 + "a") * length | before | after
------------------------------------------------+-------------+---------------
Total | 292 ms (*) | 1.93 ms (-99%)
------------------------------------------------+-------------+---------------
-----------------------------------------+-------------+---------------
surrogateescape: "\udcff" + "a" * length | before | after
-----------------------------------------+-------------+---------------
Total | 15.1 us (*) | 14.6 us
-----------------------------------------+-------------+---------------
---------------------------------+-------------+---------------
surrogatepass: "\udcff" * length | before | after
---------------------------------+-------------+---------------
Total | 3.2 ms (*) | 25.8 us (-99%)
---------------------------------+-------------+---------------
---------------------------------------+-------------+---------------
surrogatepass: "a" * length + "\udcff" | before | after
---------------------------------------+-------------+---------------
Total | 15.4 us (*) | 14.6 us (-5%)
---------------------------------------+-------------+---------------
---------------------------------------------------+-------------+---------------
surrogatepass: ("a" * 99 + "\udcff" * 99) * length | before | after
---------------------------------------------------+-------------+---------------
Total | 319 ms (*) | 5.59 ms (-98%)
---------------------------------------------------+-------------+---------------
----------------------------------------------+-------------+---------------
surrogatepass: ("\udcff" * 99 + "a") * length | before | after
----------------------------------------------+-------------+---------------
Total | 316 ms (*) | 2.8 ms (-99%)
----------------------------------------------+-------------+---------------
---------------------------------------+-------------+---------------
surrogatepass: "\udcff" + "a" * length | before | after
---------------------------------------+-------------+---------------
Total | 15.2 us (*) | 14.4 us (-5%)
---------------------------------------+-------------+---------------
------------------------------------+-------------+---------------
backslashreplace: "\udcff" * length | before | after
------------------------------------+-------------+---------------
Total | 3.52 ms (*) | 112 us (-97%)
------------------------------------+-------------+---------------
------------------------------------------+-------------+---------------
backslashreplace: "a" * length + "\udcff" | before | after
------------------------------------------+-------------+---------------
Total | 16 us (*) | 19.2 us (+20%)
------------------------------------------+-------------+---------------
------------------------------------------------------+-------------+---------------
backslashreplace: ("a" * 99 + "\udcff" * 99) * length | before | after
------------------------------------------------------+-------------+---------------
Total | 347 ms (*) | 16.9 ms (-95%)
------------------------------------------------------+-------------+---------------
-------------------------------------------------+-------------+---------------
backslashreplace: ("\udcff" * 99 + "a") * length | before | after
-------------------------------------------------+-------------+---------------
Total | 347 ms (*) | 15.3 ms (-96%)
-------------------------------------------------+-------------+---------------
------------------------------------------+-------------+---------------
backslashreplace: "\udcff" + "a" * length | before | after
------------------------------------------+-------------+---------------
Total | 15.8 us (*) | 18.8 us (+19%)
------------------------------------------+-------------+---------------
------------------------------------------------------+--------------+----------------
Summary | before | after
------------------------------------------------------+--------------+----------------
ignore: "\udcff" * length | 2.66 ms () | 8.19 us (-100%)
ignore: "a" * length + "\udcff" | 15 us () | 14.6 us
ignore: ("a" * 99 + "\udcff" * 99) * length | 265 ms () | 2.82 ms (-99%)
ignore: ("\udcff" * 99 + "a") * length | 263 ms () | 930 us (-100%)
ignore: "\udcff" + "a" * length | 14.9 us () | 14.6 us
replace: "\udcff" * length | 2.94 ms () | 8.52 us (-100%)
replace: "a" * length + "\udcff" | 18 us () | 14.7 us (-18%)
replace: ("a" * 99 + "\udcff" * 99) * length | 293 ms () | 3.21 ms (-99%)
replace: ("\udcff" * 99 + "a") * length | 290 ms () | 1.02 ms (-100%)
replace: "\udcff" + "a" * length | 15.1 us () | 14.7 us
surrogateescape: "\udcff" * length | 2.97 ms () | 16.3 us (-99%)
surrogateescape: "a" * length + "\udcff" | 15.3 us () | 14.6 us
surrogateescape: ("a" * 99 + "\udcff" * 99) * length | 295 ms () | 4.04 ms (-99%)
surrogateescape: ("\udcff" * 99 + "a") * length | 292 ms () | 1.93 ms (-99%)
surrogateescape: "\udcff" + "a" * length | 15.1 us () | 14.6 us
surrogatepass: "\udcff" * length | 3.2 ms () | 25.8 us (-99%)
surrogatepass: "a" * length + "\udcff" | 15.4 us () | 14.6 us (-5%)
surrogatepass: ("a" * 99 + "\udcff" * 99) * length | 319 ms () | 5.59 ms (-98%)
surrogatepass: ("\udcff" * 99 + "a") * length | 316 ms () | 2.8 ms (-99%)
surrogatepass: "\udcff" + "a" * length | 15.2 us () | 14.4 us (-5%)
backslashreplace: "\udcff" * length | 3.52 ms () | 112 us (-97%)
backslashreplace: "a" * length + "\udcff" | 16 us () | 19.2 us (+20%)
backslashreplace: ("a" * 99 + "\udcff" * 99) * length | 347 ms () | 16.9 ms (-95%)
backslashreplace: ("\udcff" * 99 + "a") * length | 347 ms () | 15.3 ms (-96%)
backslashreplace: "\udcff" + "a" * length | 15.8 us () | 18.8 us (+19%)
------------------------------------------------------+--------------+----------------
Total | 3.04 sec () | 54.9 ms (-98%)
------------------------------------------------------+--------------+----------------
vstinner commentedon Oct 1, 2015
Oh, the default handler for errror handlers uses a loop to check for non-ASCII characters. It can be replaced with PyUnicode_IS_ASCII(str) which has a complexity O(1). Done in new patch.
python-dev commentedon Oct 1, 2015
New changeset 2b5357b38366 by Victor Stinner in branch 'default':
Issue bpo-25267: The UTF-8 encoder is now up to 75 times as fast for error
https://hg.python.org/cpython/rev/2b5357b38366
vstinner commentedon Oct 1, 2015
I pushed my optimization.