Repository navigation
[mypyc] Cache single-character strings up to U+0FFF - #22465
Open
Jahongir-Qurbonov wants to merge 1 commit into
Open
Jahongir-Qurbonov wants to merge 1 commit into
Jahongir-Qurbonov wants to merge 1 commit into
Conversation
CPython only caches single-character Latin-1 strings, so iterating over or indexing text in other alphabetic scripts (Greek, Cyrillic, Arabic, Indic scripts, etc.) allocated and freed a new str object for every character. In compiled code the allocation dominated the cost of `for c in s`. Cache single-character strings for U+0100..U+0FFF in a table that is filled on first use and never evicted, and use it for str indexing, iteration and chr(). Larger code points such as CJK aren't cached: a small direct-mapped cache made random CJK text ~35% slower due to evictions. The cache is disabled on free-threaded builds.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CPython caches single-character strings only for Latin-1. In compiled code,
for c in s,s[i]andchr(n)therefore allocate and free a newstrfor everynon-Latin-1 character, and the allocation is most of the cost of the loop.
This adds a cache for single-character strings in U+0100..U+0FFF (Latin Extended,
Greek, Cyrillic, Armenian, Hebrew, Arabic, Indic scripts, Thai, Georgian, ...),
used by str indexing, iteration and
chr(). Entries are created on first use andnever evicted. The cache is disabled on free-threaded builds.
Code points above U+0FFF (CJK, emoji) aren't cached. I first tried a 256-entry
direct-mapped cache covering all code points: Cyrillic and emoji text got 2x faster,
but random CJK text got ~35% slower because of evictions. The fixed range has no
eviction, so text outside it isn't affected.
Memory: the table is 30 KB of zero-initialized pointers, plus 60 bytes for each
distinct character actually used (at most ~230 KB if all 3840 are used).
Benchmark
1M-character strings, ms, best of 9, CPython 3.13, Linux x86_64, gcc -O3:
for c in s: n += 1for c in s: n += ord(c)for c in s: if c == "x": ...CJK/emoji differences are within noise.
Testing
testStrSingleCharItemschecks indexing and iteration across Latin-1,the cached range and beyond, with cache hits on a second pass. It also checks that
c += "x"on a cached character doesn't modify the cached object.test_chr_code_pointsalready checkschr()for every BMP code point.str_ops.ccompiles with-Werroragainst both normal and free-threaded 3.14 headers.