gh-155525: Fix quadratic complexity in f-string tokenization - #156756
gh-155525: Fix quadratic complexity in f-string tokenization#156756gwosti wants to merge 1 commit into
Conversation
| const char *expression = tok_mode->last_expr_start; | ||
| assert(expression != NULL); | ||
| assert(expression <= tok->start); | ||
| Py_ssize_t expression_size = tok->start - expression; |
There was a problem hiding this comment.
Unless I am missing something, this also changes behaviour for a : after a !=: on main the ':' case only set last_expr_end when it was still -1, so for f'{a!=b=:>10}' the debug text was cut at the ! and t'{a!=b:>10}'.interpolations[0].expression was 'a'. Now we always take everything up to tok->start, which is the right thing, but can we add a test for both cases so we don't lose it?
| source = ''.join( | ||
| f"value_{i} = f'{fields}'\n" for i in range(1_000) | ||
| ) | ||
| compile(source, '<string>', 'exec') |
There was a problem hiding this comment.
Nit: this only checks that compile does not blow up, so a regression here would only show up as a slow test. Maybe we can exec it with the x* names defined and check a couple of the value_* results (same for the t-string one)?
| Py_ssize_t last_expr_size; | ||
| Py_ssize_t last_expr_end; | ||
| char* last_expr_buffer; | ||
| const char* last_expr_start; |
There was a problem hiding this comment.
Nit: can we add a comment here saying that this points into tok->buf and relies on _PyTok_ReaderUnderflow never resetting the buffer while INSIDE_FSTRING(tok)? That invariant is what makes this work and it lives a bit far from here.
Retain the start of each f-string and t-string replacement expression in the tokenizer buffer instead of allocating a temporary buffer and copying the remaining source for every replacement field. If the tokenizer buffer is resized, preserve the expression start as an offset and restore it afterward.
Microbenchmarks
--enable-optimizations --with-lto, Linux x86-64compile()ast.parse()Benchmark script
compile()/ast.parse()time for modules with many f-strings (PEP 701 tokenizerupdate_fstring_exprrescans the whole remaining buffer per replacement field) #155525