gh-151907: Avoid creating temporary objects in some comprehensions#151908
gh-151907: Avoid creating temporary objects in some comprehensions#151908ZeroIntensity wants to merge 10 commits into
Conversation
| VISIT(c, expr, elt); | ||
| ADDOP(c, elt_loc, POP_TOP); | ||
| break; | ||
| } |
There was a problem hiding this comment.
Can we instead just wrap the LIST_APPEND in if !avoid_creation?
There was a problem hiding this comment.
I think we'd also need to wrap the LIST_EXTEND with that too; I opted for this approach to avoid duplicating that logic.
But do we need the separate VISIT path for the Starred case at all? I might be able to refactor it to look like this:
ISIT(c, expr, elt);
if (elt->kind == Starred_kind) {
op = LIST_EXTEND;
}
else {
op = LIST_APPEND;
}
if (!avoid_creation) {
ADDOP_I(c, elt_loc, op, depth + 1);
}|
I think this might change semantics for the dict case - if a key is not hashable. |
|
Yeah, good catch. This breaks {x for x in [[]]} # No error!I'll just remove |
This was a breaking change because hash() was no longer called on the elements.
Maybe also add tests for these cases. |
| ] | ||
| self.codegen_test(snippet, expected) | ||
|
|
||
| def test_no_target_comp_optimization(self): |
There was a problem hiding this comment.
I believe you need to add a test where the loop body has a side effect. IIUC, the body with thread.join() should be turned into a method call, rather than just pass.
|
The async case is not handled correctly here. This segfaults: |
|
Fixed. |
Avoids an "env-changed" error with regrtest.
| if (value->kind == ListComp_kind) { | ||
| /* Optimization: Don't bother creating structures if they won't be | ||
| * used. */ | ||
| return codegen_listcomp_impl(c, value, /*avoid_creation=*/true); |
There was a problem hiding this comment.
It might be nicer if we were not calling codegen_listcomp_impl here but rather setting a boolean on the compiler_unit struct to track whether we are in the topmost expression or not. What do you think?
There was a problem hiding this comment.
Not opposed to the idea, but it feels slightly weird in cases where the thing being compiled isn't an expression. A compiler->has_no_target flag in a FunctionDef, for example, has no real meaning (because compiler->has_no_target == 0 would imply "has target", but that doesn't make sense for a statement). I do think it'll make future optimizations like this easier, though.
There was a problem hiding this comment.
I was thinking of a flag like is_subexpression.
There was a problem hiding this comment.
How would I implement that? I can't do like METADATA(c)->u_is_subexpression = 1 in codegen_stmt_expr because that would also affect listcomps inside the one we're optimizing (like [x for x in [y for y in z]]). I could add a stack or something like that, but that would significantly increase the complexity of this PR.
Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
The following code:
is essentially turned into: