-
-
Notifications
You must be signed in to change notification settings - Fork 31.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gh-132121: Always escape non-printable characters in pygettext
#132122
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please measure performance of escape_ascii()
for non-ASCII strings of different length (10 to 100000)?
This is on a PGO build:
test code: def test_escape_ascii(string):
for _ in range(100):
escape_ascii(string, 'utf-8')
make_escapes(True)
for n in (10, 100, 1_000, 10_000, 100_000):
print(f"{n=}")
string = 'α' * n
print(f"\tonly printable: {timeit.timeit(partial(test_escape_ascii, string), number=10)}")
string = '\302\200' * n
print(f"\tonly non-printable: {timeit.timeit(partial(test_escape_ascii, string), number=10)}")
string = 'α' * ((9*n)//10) + '\302\200' * (n//10)
print(f"\tmix (90% printable, 10% non-printable): {timeit.timeit(partial(test_escape_ascii, string), number=10)}")
string = 'α' * (n//2) + '\302\200' * (n//2)
print(f"\tmix (50% printable, 50% non-printable): {timeit.timeit(partial(test_escape_ascii, string), number=10)}") Strings that only contain non-printable characters are much slower with this PR, though they should be quite uncommon. I also tested mixed strings (50% printable/non-printable and 90%/10% printable/non-printable) where the numbers look more reasonable but maybe there's a way to optimize the function? |
Thanks. It is expected that non-printable characters should be slower, but non-ASCII non-printable characters should be extremely rare. I worried about printable characters. How is this in comparison with the current code? The time grow looks linear, but this may be CPython specific optimization, on other implementations it may be quadratic. I am asking because you changed the implementation to use |
Ah, I see, you already tested it against main and PR. The difference is insignificant. Could you please test it on PyPy? Just to be sure that the worst case is not so bad. |
Will do, either today or tomorrow :) |
On PyPy 3.11 it regresses by quite a bit (I'll only include
This is a pretty big regression. I rewrote the function back to a list comprehension and the numbers look much better:
def escape_ascii(s, encoding):
return ''.join(escapes[ord(c)] if ord(c) < 128 else c
if c.isprintable() else escape_nonascii(c, encoding)
for c in s) I'll update the PR to keep the list comprehension. |
Thank you. So there was a difference. BTW, there is an error in your benchmarks for non-printable characters. The changes in |
Ah, you're right, thanks for pointing it out!
This change makes it so that the |
Ah, good point! LGTM. Thank you for your PR. |
Thanks for the review! |
Pretty much the title, all non-printable characters are always escaped regardless whether
--escape
is passed or not.--escape
option #132121