-
-
Notifications
You must be signed in to change notification settings - Fork 35k
gh-151907: Avoid creating temporary objects in some comprehensions #151908
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
Changes from 4 commits
b4a6d81
77251a2
d80384a
d2749ae
cf1bb55
1988b90
c010589
c1aa785
a750ed5
cdbcfad
5d9ccf3
ad24c19
9fe6718
e86a7be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Avoid creating :class:`list`, :class:`set`, and :class:`dict` objects in | ||
| comprehensions when the comprehension is not used as a value. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -233,12 +233,18 @@ typedef enum { | |
| ITERATOR_ON_STACK = 2, | ||
| } IterStackPosition; | ||
|
|
||
| static int | ||
| codegen_listcomp_impl(compiler *c, expr_ty e, bool avoid_creation); | ||
| static int | ||
| codegen_setcomp_impl(compiler *c, expr_ty e, bool avoid_creation); | ||
| static int | ||
| codegen_dictcomp_impl(compiler *c, expr_ty e, bool avoid_creation); | ||
| static int codegen_sync_comprehension_generator( | ||
| compiler *c, location loc, | ||
| asdl_comprehension_seq *generators, int gen_index, | ||
| int depth, | ||
| expr_ty elt, expr_ty val, int type, | ||
| IterStackPosition iter_pos); | ||
| IterStackPosition iter_pos, bool avoid_creation); | ||
|
|
||
| static int codegen_async_comprehension_generator( | ||
| compiler *c, location loc, | ||
|
|
@@ -3084,6 +3090,20 @@ codegen_stmt_expr(compiler *c, location loc, expr_ty value) | |
| return SUCCESS; | ||
| } | ||
|
|
||
| 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); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be nicer if we were not calling
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not opposed to the idea, but it feels slightly weird in cases where the thing being compiled isn't an expression. A
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking of a flag like
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How would I implement that? I can't do like
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about something like this: and then in Then
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like that idea. I just pushed it, let me know what you think. |
||
| } | ||
|
|
||
| if (value->kind == SetComp_kind) { | ||
| return codegen_setcomp_impl(c, value, /*avoid_creation=*/true); | ||
| } | ||
|
|
||
| if (value->kind == DictComp_kind) { | ||
| return codegen_dictcomp_impl(c, value, /*avoid_creation=*/true); | ||
| } | ||
|
|
||
| VISIT(c, expr, value); | ||
| ADDOP(c, NO_LOCATION, POP_TOP); /* artificial */ | ||
| return SUCCESS; | ||
|
|
@@ -4541,7 +4561,7 @@ codegen_comprehension_generator(compiler *c, location loc, | |
| asdl_comprehension_seq *generators, int gen_index, | ||
| int depth, | ||
| expr_ty elt, expr_ty val, int type, | ||
| IterStackPosition iter_pos) | ||
| IterStackPosition iter_pos, bool avoid_creation) | ||
| { | ||
| comprehension_ty gen; | ||
| gen = (comprehension_ty)asdl_seq_GET(generators, gen_index); | ||
|
|
@@ -4552,7 +4572,7 @@ codegen_comprehension_generator(compiler *c, location loc, | |
| } else { | ||
| return codegen_sync_comprehension_generator( | ||
| c, loc, generators, gen_index, depth, elt, val, type, | ||
| iter_pos); | ||
| iter_pos, avoid_creation); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -4561,7 +4581,7 @@ codegen_sync_comprehension_generator(compiler *c, location loc, | |
| asdl_comprehension_seq *generators, | ||
| int gen_index, int depth, | ||
| expr_ty elt, expr_ty val, int type, | ||
| IterStackPosition iter_pos) | ||
| IterStackPosition iter_pos, bool avoid_creation) | ||
| { | ||
| /* generate code for the iterator, then each of the ifs, | ||
| and then write to the element */ | ||
|
|
@@ -4629,7 +4649,7 @@ codegen_sync_comprehension_generator(compiler *c, location loc, | |
| RETURN_IF_ERROR( | ||
| codegen_comprehension_generator(c, loc, | ||
| generators, gen_index, depth, | ||
| elt, val, type, ITERABLE_IN_LOCAL)); | ||
| elt, val, type, ITERABLE_IN_LOCAL, avoid_creation)); | ||
| } | ||
|
|
||
| location elt_loc = LOC(elt); | ||
|
|
@@ -4639,6 +4659,7 @@ codegen_sync_comprehension_generator(compiler *c, location loc, | |
| /* comprehension specific code */ | ||
| switch (type) { | ||
| case COMP_GENEXP: | ||
| assert(!avoid_creation); | ||
| if (elt->kind == Starred_kind) { | ||
| NEW_JUMP_TARGET_LABEL(c, unpack_start); | ||
| NEW_JUMP_TARGET_LABEL(c, unpack_end); | ||
|
|
@@ -4660,6 +4681,11 @@ codegen_sync_comprehension_generator(compiler *c, location loc, | |
| } | ||
| break; | ||
| case COMP_LISTCOMP: | ||
| if (avoid_creation) { | ||
| VISIT(c, expr, elt); | ||
| ADDOP(c, elt_loc, POP_TOP); | ||
| break; | ||
| } | ||
|
iritkatriel marked this conversation as resolved.
|
||
| if (elt->kind == Starred_kind) { | ||
| VISIT(c, expr, elt->v.Starred.value); | ||
| ADDOP_I(c, elt_loc, LIST_EXTEND, depth + 1); | ||
|
|
@@ -4670,6 +4696,11 @@ codegen_sync_comprehension_generator(compiler *c, location loc, | |
| } | ||
| break; | ||
| case COMP_SETCOMP: | ||
| if (avoid_creation) { | ||
| VISIT(c, expr, elt); | ||
| ADDOP(c, elt_loc, POP_TOP); | ||
| break; | ||
| } | ||
| if (elt->kind == Starred_kind) { | ||
| VISIT(c, expr, elt->v.Starred.value); | ||
| ADDOP_I(c, elt_loc, SET_UPDATE, depth + 1); | ||
|
|
@@ -4680,6 +4711,11 @@ codegen_sync_comprehension_generator(compiler *c, location loc, | |
| } | ||
| break; | ||
| case COMP_DICTCOMP: | ||
| if (avoid_creation) { | ||
| VISIT(c, expr, elt); | ||
| ADDOP(c, elt_loc, POP_TOP); | ||
| break; | ||
| } | ||
| if (val == NULL) { | ||
| /* unpacking (**) case */ | ||
| VISIT(c, expr, elt); | ||
|
|
@@ -4773,7 +4809,7 @@ codegen_async_comprehension_generator(compiler *c, location loc, | |
| RETURN_IF_ERROR( | ||
| codegen_comprehension_generator(c, loc, | ||
| generators, gen_index, depth, | ||
| elt, val, type, 0)); | ||
| elt, val, type, 0, false)); | ||
| } | ||
|
|
||
| location elt_loc = LOC(elt); | ||
|
|
@@ -4996,7 +5032,7 @@ pop_inlined_comprehension_state(compiler *c, location loc, | |
| static int | ||
| codegen_comprehension(compiler *c, expr_ty e, int type, | ||
| identifier name, asdl_comprehension_seq *generators, expr_ty elt, | ||
| expr_ty val) | ||
| expr_ty val, bool avoid_creation) | ||
| { | ||
| PyCodeObject *co = NULL; | ||
| _PyCompile_InlinedComprehensionState inline_state = {NULL, NULL, NULL, NO_LABEL}; | ||
|
|
@@ -5070,20 +5106,27 @@ codegen_comprehension(compiler *c, expr_ty e, int type, | |
| goto error_in_scope; | ||
| } | ||
|
|
||
| ADDOP_I(c, loc, op, 0); | ||
| if (is_inlined) { | ||
| ADDOP_I(c, loc, SWAP, 2); | ||
| if (!avoid_creation) { | ||
| ADDOP_I(c, loc, op, 0); | ||
| if (is_inlined) { | ||
| ADDOP_I(c, loc, SWAP, 2); | ||
| } | ||
| } else { | ||
| ADDOP_I(c, loc, COPY, 1); | ||
|
iritkatriel marked this conversation as resolved.
|
||
| } | ||
| } | ||
| if (codegen_comprehension_generator(c, loc, generators, 0, 0, | ||
| elt, val, type, iter_state) < 0) { | ||
| elt, val, type, iter_state, avoid_creation) < 0) { | ||
| goto error_in_scope; | ||
| } | ||
|
|
||
| if (is_inlined) { | ||
| if (pop_inlined_comprehension_state(c, loc, &inline_state)) { | ||
| goto error; | ||
| } | ||
| if (avoid_creation) { | ||
| ADDOP(c, loc, POP_TOP); | ||
| } | ||
| return SUCCESS; | ||
| } | ||
|
|
||
|
|
@@ -5118,6 +5161,8 @@ codegen_comprehension(compiler *c, expr_ty e, int type, | |
| ADD_YIELD_FROM(c, loc, 1); | ||
| } | ||
|
|
||
| assert(!avoid_creation); | ||
|
|
||
| return SUCCESS; | ||
| error_in_scope: | ||
| if (!is_inlined) { | ||
|
|
@@ -5133,44 +5178,67 @@ codegen_comprehension(compiler *c, expr_ty e, int type, | |
| } | ||
|
|
||
| static int | ||
| codegen_genexp(compiler *c, expr_ty e) | ||
| codegen_genexp_impl(compiler *c, expr_ty e, bool avoid_creation) | ||
| { | ||
| assert(e->kind == GeneratorExp_kind); | ||
| _Py_DECLARE_STR(anon_genexpr, "<genexpr>"); | ||
| return codegen_comprehension(c, e, COMP_GENEXP, &_Py_STR(anon_genexpr), | ||
| e->v.GeneratorExp.generators, | ||
| e->v.GeneratorExp.elt, NULL); | ||
| e->v.GeneratorExp.elt, NULL, avoid_creation); | ||
| } | ||
|
|
||
| static int | ||
| codegen_listcomp(compiler *c, expr_ty e) | ||
| codegen_genexp(compiler *c, expr_ty e) | ||
| { | ||
| return codegen_genexp_impl(c, e, false); | ||
| } | ||
|
|
||
| static int | ||
| codegen_listcomp_impl(compiler *c, expr_ty e, bool avoid_creation) | ||
| { | ||
| assert(e->kind == ListComp_kind); | ||
| _Py_DECLARE_STR(anon_listcomp, "<listcomp>"); | ||
| return codegen_comprehension(c, e, COMP_LISTCOMP, &_Py_STR(anon_listcomp), | ||
| e->v.ListComp.generators, | ||
| e->v.ListComp.elt, NULL); | ||
| e->v.ListComp.elt, NULL, avoid_creation); | ||
| } | ||
|
|
||
| static int | ||
| codegen_setcomp(compiler *c, expr_ty e) | ||
| codegen_listcomp(compiler *c, expr_ty e) | ||
| { | ||
| return codegen_listcomp_impl(c, e, false); | ||
| } | ||
|
|
||
| static int | ||
| codegen_setcomp_impl(compiler *c, expr_ty e, bool avoid_creation) | ||
| { | ||
| assert(e->kind == SetComp_kind); | ||
| _Py_DECLARE_STR(anon_setcomp, "<setcomp>"); | ||
| return codegen_comprehension(c, e, COMP_SETCOMP, &_Py_STR(anon_setcomp), | ||
| e->v.SetComp.generators, | ||
| e->v.SetComp.elt, NULL); | ||
| e->v.SetComp.elt, NULL, avoid_creation); | ||
| } | ||
|
|
||
| static int | ||
| codegen_setcomp(compiler *c, expr_ty e) | ||
| { | ||
| return codegen_setcomp_impl(c, e, false); | ||
| } | ||
|
|
||
| static int | ||
| codegen_dictcomp(compiler *c, expr_ty e) | ||
| codegen_dictcomp_impl(compiler *c, expr_ty e, bool avoid_creation) | ||
| { | ||
| assert(e->kind == DictComp_kind); | ||
| _Py_DECLARE_STR(anon_dictcomp, "<dictcomp>"); | ||
| return codegen_comprehension(c, e, COMP_DICTCOMP, &_Py_STR(anon_dictcomp), | ||
| e->v.DictComp.generators, | ||
| e->v.DictComp.key, e->v.DictComp.value); | ||
| e->v.DictComp.key, e->v.DictComp.value, avoid_creation); | ||
| } | ||
|
|
||
| static int | ||
| codegen_dictcomp(compiler *c, expr_ty e) | ||
| { | ||
| return codegen_dictcomp_impl(c, e, false); | ||
| } | ||
|
|
||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.