GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — gh-129813, PEP 782: Use PyBytesWriter in utf8_encoder() by vstinner · Pull Request #138874 · python/cpython Conversation
|
|
Microbenchmark on the UTF-8 encoder: import pyperf runner = pyperf.Runner() runner.timeit('abc', setup='s="abc"', stmt='s.encode()') runner.timeit('a x 1000', setup='s="a" * 1000', stmt='s.encode()') runner.timeit('ab<surrogate> [namereplace]', setup=r's="ab\udc80"', stmt='s.encode(errors="namereplace")') runner.timeit('ab<surrogate> [ignore]', setup=r's="ab\udc80"', stmt='s.encode(errors="ignore")') runner.timeit('(a<surrogate>) x 1000 [namereplace]', setup=r's="a\udc80" * 1000', stmt='s.encode(errors="namereplace")') runner.timeit('(a<surrogate>) x 1000 [ignore]', setup=r's="a\udc80" * 1000', stmt='s.encode(errors="ignore")') Results:
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Please make benchmarks for non-ASCII strings. Consider different ranges (which represents different internal representations and lenghts of UTF-8 representation):
Consider also strings which contain one character from the higher range (for example, 0x10000 and all other characters ASCII, etc). |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
More benchmark: import pyperf runner = pyperf.Runner() ranges = ( (r'\0', r'\x7f'), (r'\x80', r'\xff'), (r'\u0400', r'\u0fff'), (r'\U00010000', r'\u0010ffff'), ) for first, last in ranges: runner.timeit(f'"{first}{last}"', setup=f"first='{first}'; last='{last}'; s=first+last", stmt='s.encode()') for length in (5, 50, 500): for first, last in ranges: runner.timeit(f'"{first}{last}" * {length}', setup=f"first='{first}'; last='{last}'; s=(first+last) * {length}", stmt='s.encode()') Results:
Benchmark hidden because not significant (5): "\U00010000\u0010ffff", "\u0400\u0fff" * 5, "\x80\xff" * 50, "\0\x7f" * 500, "\U00010000\u0010ffff" * 500 |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Benchmark: import pyperf runner = pyperf.Runner() for length in (5, 50, 500): runner.timeit(f'"x" * {length} + chr(0x10000)', setup=f's="x" * {length} + chr(0x10000)', stmt='s.encode()') Results:
Benchmark hidden because not significant (1): "x" * 500 + chr(0x10000) |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@serhiy-storchaka: The difference is about a "few nanoseconds", around +10 ns in the worst case, 1.08x faster in the best case. Do you think that it's acceptable? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Some overhead is caused by dynamic memory allocation for PyBytesWriter -- it is unavoidable. But there may be a loss due to losing fine control on overallocation -- I need to look at it closer. Maybe it can be avoided by adding a new C API.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
If this loss can be measured, I would suggest adding a private API to disable overallocation. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Updated benchmark on the worst case: import pyperf runner=pyperf.Runner() runner.timeit('utf8', setup=r's="\uFFFF"*(256//3)+"\uDC80"', stmt='s.encode(errors="backslashreplace")') runner.timeit('latin1', setup=r"s=('a'*255+'\u0100')", stmt="s.encode('latin1', 'backslashreplace')") Result:
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@serhiy-storchaka: utf8_encoder() and unicode_encode_ucs1() are the last 2 functions using the private API. I would like to merge this change to be able to remove the private API, even if there is an overhead on performance. On the common cases, there is no significant impact on performance. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I updated the PR to keep the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
More benchmarks on the corner cases. Benchmark: Result: Benchmark: Result: |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I updated the PR to reimplement the min_size micro-optimization. There is no more 1.3x slowdown. I also recomputed all benchmark results on the latest PR version. Results are now between 1.08x slower and 1.08x faster. Most benchmarks are in the [-5%, +5%] range which can be associated to noise in the benchmark (can be ignored). @serhiy-storchaka: I plan to merge this change next week. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
LGTM. 👍
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Remove useless PyBytesWriter_Discard() call
|
Merged, thanks for the review @serhiy-storchaka. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.