Optimizing literal comparisons and contains
Many operations involving two literals are optimized (to a certain level). So it sort of surprises me that literal comparisons are not optimized and literal contains only convert the right operand to a constant if possible. I'd like to implement optimizations for these especially for the literal contains. There is a TODO in the ast optimizer for literal comparisons as well, and that's another reason I would like to have these added.
Many operations involving two literals are optimized (to a certain level). So it sort of surprises me that literal comparisons are not optimized and literal contains only convert the right operand to a constant if possible. I'd like to implement optimizations for these especially for the literal contains. There is a TODO in the ast optimizer for literal comparisons as well, and that's another reason I would like to have these added.
Though not having paid much attention to this over the years, I'm pretty sure I've seen the topic float past from time-to-time. As I recall, optimizing expressions involving two constants isn't a big win in Python because that sort of expression occurs rarely in anything other than test code. In C/C++ and its cousins, preprocessors routinely expand names to constants, so 2 == 2 might well be seen by the compiler even though the author actually wrote MUMBLE == FRAZZLE If a Python programmer wrote such an expression, MUMBLE and FRAZZLE would be names bound to a particular object, and could — theoretically — be rebound at runtime, so such an expression couldn't safely be optimized. So, while you could optimize expressions involving just constants, the benefit would be exceedingly small compared to the effort to write and maintain the optimization code. Skip
For the benefit of the audience on python-dev, you should also mention that this proposal and associated PR has been twice discussed and rejected on the tracker: https://bugs.python.org/issue45907 https://bugs.python.org/issue45843 The response just given by Skip pretty much matches the comments already given by Batuhan, Pablo, and Serhiy. So far, no one who has looked at this thinks this should be done.
On 28/11/2021 6:28 am, raymond.hettinger@gmail.com wrote:
For the benefit of the audience on python-dev, you should also mention that this proposal and associated PR has been twice discussed and rejected on the tracker:
https://bugs.python.org/issue45907 https://bugs.python.org/issue45843
The response just given by Skip pretty much matches the comments already given by Batuhan, Pablo, and Serhiy. So far, no one who has looked at this thinks this should be done.
That is not entirely true: https://github.com/python/cpython/pull/29639#issuecomment-974146979
That is not entirely true: https://github.com/python/cpython/pull/29639#issuecomment-974146979
The only places I've seen "if 0:" or "if False:" in live code was for debugging. Optimizing that hardly seems necessary. In any case, the original comment was about comparisons of two constants. I suppose sweeping up all of that into a constant expression folding/elimination step performed on the AST and/or during peephole optimization would cover both cases. Skip
28.11.21 17:13, Skip Montanaro пише:
That is not entirely true: https://github.com/python/cpython/pull/29639#issuecomment-974146979
The only places I've seen "if 0:" or "if False:" in live code was for debugging. Optimizing that hardly seems necessary. In any case, the original comment was about comparisons of two constants. I suppose sweeping up all of that into a constant expression folding/elimination step performed on the AST and/or during peephole optimization would cover both cases.
"if 0:" and "if False:" is already optimized by the compiler. The OP proposes to optimize "2 < 1", and I cannot imagine any use case for this.
On Nov 28, 2021, at 3:03 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
28.11.21 17:13, Skip Montanaro пише:
That is not entirely true: https://github.com/python/cpython/pull/29639#issuecomment-974146979
The only places I've seen "if 0:" or "if False:" in live code was for debugging. Optimizing that hardly seems necessary. In any case, the original comment was about comparisons of two constants. I suppose sweeping up all of that into a constant expression folding/elimination step performed on the AST and/or during peephole optimization would cover both cases.
"if 0:" and "if False:" is already optimized by the compiler. The OP proposes to optimize "2 < 1", and I cannot imagine any use case for this.
I agree. I suggest we don’t add this optimization. Eric
I am slightly surprised that it seems to be *easier* to fold selected constant expressions than to have more generic code to fold them all. Or at least, all those that don't contain containers, such as 1 in [0,1,2] Rob Cliffe On 28/11/2021 21:10, Eric V. Smith wrote:
On Nov 28, 2021, at 3:03 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
That is not entirely true: https://github.com/python/cpython/pull/29639#issuecomment-974146979 The only places I've seen "if 0:" or "if False:" in live code was for debugging. Optimizing that hardly seems necessary. In any case, the original comment was about comparisons of two constants. I suppose sweeping up all of that into a constant expression folding/elimination step performed on the AST and/or during peephole optimization would cover both cases. "if 0:" and "if False:" is already optimized by the compiler. The OP
28.11.21 17:13, Skip Montanaro пише: proposes to optimize "2 < 1", and I cannot imagine any use case for this. I agree. I suggest we don’t add this optimization.
Eric _______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/JP6FF2RH... Code of Conduct: http://python.org/psf/codeofconduct/
I will frequently do simple computation with literals to make my code more clear: t = 2 * 3600 # 2 hours in seconds But I see no need to optimize this kind of thing -- it would never be in a tight loop. -CHB On Sun, Nov 28, 2021 at 3:06 PM Rob Cliffe via Python-Dev < python-dev@python.org> wrote:
I am slightly surprised that it seems to be *easier* to fold selected constant expressions than to have more generic code to fold them all. Or at least, all those that don't contain containers, such as 1 in [0,1,2] Rob Cliffe
On Nov 28, 2021, at 3:03 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
That is not entirely true: https://github.com/python/cpython/pull/29639#issuecomment-974146979 The only places I've seen "if 0:" or "if False:" in live code was for debugging. Optimizing that hardly seems necessary. In any case, the original comment was about comparisons of two constants. I suppose sweeping up all of that into a constant expression folding/elimination step performed on the AST and/or during peephole optimization would cover both cases. "if 0:" and "if False:" is already optimized by the compiler. The OP
28.11.21 17:13, Skip Montanaro пише: proposes to optimize "2 < 1", and I cannot imagine any use case for
On 28/11/2021 21:10, Eric V. Smith wrote: this.
I agree. I suggest we don’t add this optimization.
Eric _______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/JP6FF2RH... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/LD3XHINO... Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
On 11/28/2021 7:52 PM, Christopher Barker wrote:
I will frequently do simple computation with literals to make my code more clear:
t = 2 * 3600 # 2 hours in seconds
That is optimized as you'd hope. Tested in 3.8:
dis.dis("t = 2 * 3600") 1 0 LOAD_CONST 0 (7200) 2 STORE_NAME 0 (t) 4 LOAD_CONST 1 (None) 6 RETURN_VALUE
Eric
But I see no need to optimize this kind of thing -- it would never be in a tight loop.
-CHB
On Sun, Nov 28, 2021 at 3:06 PM Rob Cliffe via Python-Dev <python-dev@python.org> wrote:
I am slightly surprised that it seems to be *easier* to fold selected constant expressions than to have more generic code to fold them all. Or at least, all those that don't contain containers, such as 1 in [0,1,2] Rob Cliffe
On 28/11/2021 21:10, Eric V. Smith wrote: >> On Nov 28, 2021, at 3:03 PM, Serhiy Storchaka <storchaka@gmail.com> wrote: >> >> 28.11.21 17:13, Skip Montanaro пише: >>>> That is not entirely true: https://github.com/python/cpython/pull/29639#issuecomment-974146979 >>> The only places I've seen "if 0:" or "if False:" in live code was for >>> debugging. Optimizing that hardly seems necessary. In any case, the >>> original comment was about comparisons of two constants. I suppose >>> sweeping up all of that into a constant expression folding/elimination >>> step performed on the AST and/or during peephole optimization would >>> cover both cases. >> "if 0:" and "if False:" is already optimized by the compiler. The OP >> proposes to optimize "2 < 1", and I cannot imagine any use case for this. > I agree. I suggest we don’t add this optimization. > > Eric > _______________________________________________ > Python-Dev mailing list -- python-dev@python.org > To unsubscribe send an email to python-dev-leave@python.org > https://mail.python.org/mailman3/lists/python-dev.python.org/ > Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/JP6FF2RH... > Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/LD3XHINO... Code of Conduct: http://python.org/psf/codeofconduct/
-- Christopher Barker, PhD (Chris)
Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
_______________________________________________ Python-Dev mailing list --python-dev@python.org To unsubscribe send an email topython-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived athttps://mail.python.org/archives/list/python-dev@python.org/message/NJWCKNV4... Code of Conduct:http://python.org/psf/codeofconduct/
On 2021-11-29 00:52, Christopher Barker wrote:
I will frequently do simple computation with literals to make my code more clear:
t = 2 * 3600 # 2 hours in seconds
But I see no need to optimize this kind of thing -- it would never be in a tight loop.
The suggestion was specifically about optimising comparison of literals such as 1 < 2, which is not done at present, and the objection is basically YAGNI - that kind of thing is just too rare to be worth it.
On Mon, Nov 29, 2021 at 10:11 AM Rob Cliffe via Python-Dev <python-dev@python.org> wrote:
I am slightly surprised that it seems to be *easier* to fold selected constant expressions than to have more generic code to fold them all. Or at least, all those that don't contain containers, such as 1 in [0,1,2]
This is optimized to a tuple rather than a list (and if you used a set instead of a list, it'd be optimized to a frozenset), but the 'in' comparison isn't optimized. The common case where it's "x in [0,1,2]" is optimized as far as it can be. ChrisA
27.11.21 15:47, Jeremiah Vivian пише:
Many operations involving two literals are optimized (to a certain level). So it sort of surprises me that literal comparisons are not optimized and literal contains only convert the right operand to a constant if possible. I'd like to implement optimizations for these especially for the literal contains. There is a TODO in the ast optimizer for literal comparisons as well, and that's another reason I would like to have these added.
Only these operations were optimized which were necessary or very useful. The Python parser produces only sign-less numbers, so it was necessary to optimize unary minus to make -5 as fast as 5. Binary plus and minus are necessary to make complex constants. "**" and "<<" are often used in expressions for limits, masks and flags (5*10**6, 2**32-1, 1<<12). There is a benefit of computing such constants at compile time. "*" and "/" can be used to create self-documenting constats too (24*60*60, 1/3). And finally, indexing of bytes objects gets an ASCII code of the character (b'a'[0]), it is useful too.
Hi, I am surprised by the insistence on this thread for excluding comparisons from constant folding. Why should we special case comparisons? Am I missing something here? We already constant fold a variety of expressions 0 * 7 '' * 7 True - True True * False (All the above are falsey) Excluding 1 < 2 seems inconsistent. Cheers, Mark.
On Mon, 29 Nov 2021 12:32:19 +0000 Mark Shannon <mark@hotpy.org> wrote:
Hi,
I am surprised by the insistence on this thread for excluding comparisons from constant folding. Why should we special case comparisons? Am I missing something here?
Is it actually special-cased or is it just not implemented? IMHO at compile-time there's not much potential (for Python, which doesn't have "true" constants). At run-time and based on specialization, there could be. Regards Antoine.
We already constant fold a variety of expressions
0 * 7 '' * 7 True - True True * False
(All the above are falsey)
Excluding 1 < 2 seems inconsistent.
Cheers, Mark.
On Mon, Nov 29, 2021 at 12:32:19PM +0000, Mark Shannon wrote:
Hi,
I am surprised by the insistence on this thread for excluding comparisons from constant folding. Why should we special case comparisons? Am I missing something here?
We[1] are worried that the benefit gained will not be worth the maintenance burden of constant folding comparisons. Unlike constant-folding arithmetic expressions, the benefit for comparisons is small: code that compares two literals e.g. `3 < 5` is probably very rare, outside of tests. So this will help almost nobody, but still require maintenance. (And the tests will need to be changed, if we add this, otherwise they will only be testing the keyhole optimizer, not the runtime comparison!) I have no idea of how much maintenance the keyhole optimizer requires. We have a volunteer willing to do the work (Jeremy). Do we have a core developer willing to review their work, and mentor them if if it is not up to standard? If not, then this conversation will go nowhere. [1] That's an editorial "we". Personally, I don't have an opinion one way or another :-) -- Steve
You should consider "no longer have to justify why it's not optimized" as a clear benefit of making this change :-) This optimization is proposed once a year for many years... For me, any possible compilation-ahead optimization (which doesn't break the Python semantics) is worth it ;-) It's done once, possible even before the program is run for the first time, and then makes the code faster. Victor
29.11.21 18:36, Victor Stinner пише:
You should consider "no longer have to justify why it's not optimized" as a clear benefit of making this change :-) This optimization is proposed once a year for many years...
For me, any possible compilation-ahead optimization (which doesn't break the Python semantics) is worth it ;-) It's done once, possible even before the program is run for the first time, and then makes the code faster.
I consider the cost of rejecting such idea (this is actually the first time it was pushed so persistent) less than the cost of implementing and maintaining it. The proposed PR adds around 200 lines of code. It is almost 20% of the current size of ast_opt.c, it is not small. And just at first look I see a flaw in it -- it will emit a BytesWarning when compare strings and bytes. After fixing this the size will grow even more. And there may be other issues with this code which will be found months later. Later, when we rewrite the optimizer or change the structure of AST we will need to update this code too, with possibility to introduce new bugs. On other hand, the benefit of this optimization is exact zero. It does not make code faster, because it optimizes the code not used in real programs.
I agree with Serhiy's analysis. On Mon, 29 Nov 2021 at 17:10, Serhiy Storchaka <storchaka@gmail.com> wrote:
29.11.21 18:36, Victor Stinner пише:
You should consider "no longer have to justify why it's not optimized" as a clear benefit of making this change :-) This optimization is proposed once a year for many years...
For me, any possible compilation-ahead optimization (which doesn't break the Python semantics) is worth it ;-) It's done once, possible even before the program is run for the first time, and then makes the code faster.
I consider the cost of rejecting such idea (this is actually the first time it was pushed so persistent) less than the cost of implementing and maintaining it. The proposed PR adds around 200 lines of code. It is almost 20% of the current size of ast_opt.c, it is not small. And just at first look I see a flaw in it -- it will emit a BytesWarning when compare strings and bytes. After fixing this the size will grow even more. And there may be other issues with this code which will be found months later. Later, when we rewrite the optimizer or change the structure of AST we will need to update this code too, with possibility to introduce new bugs.
On other hand, the benefit of this optimization is exact zero. It does not make code faster, because it optimizes the code not used in real programs.
_______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/LOIK6ZX4... Code of Conduct: http://python.org/psf/codeofconduct/
I guess that you're talking about https://github.com/python/cpython/pull/29810/files To be honest, I never looked into Python/ast_opt.c. I expected a shorter implementation, a copy/paste or a few lines additions of "x == y" optimization. But it seems like "1 == 1" is not optimized neither. Oh. I made the assumptions that other operations were already optimized. Victor On Mon, Nov 29, 2021 at 6:11 PM Serhiy Storchaka <storchaka@gmail.com> wrote:
29.11.21 18:36, Victor Stinner пише:
You should consider "no longer have to justify why it's not optimized" as a clear benefit of making this change :-) This optimization is proposed once a year for many years...
For me, any possible compilation-ahead optimization (which doesn't break the Python semantics) is worth it ;-) It's done once, possible even before the program is run for the first time, and then makes the code faster.
I consider the cost of rejecting such idea (this is actually the first time it was pushed so persistent) less than the cost of implementing and maintaining it. The proposed PR adds around 200 lines of code. It is almost 20% of the current size of ast_opt.c, it is not small. And just at first look I see a flaw in it -- it will emit a BytesWarning when compare strings and bytes. After fixing this the size will grow even more. And there may be other issues with this code which will be found months later. Later, when we rewrite the optimizer or change the structure of AST we will need to update this code too, with possibility to introduce new bugs.
On other hand, the benefit of this optimization is exact zero. It does not make code faster, because it optimizes the code not used in real programs.
_______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/LOIK6ZX4... Code of Conduct: http://python.org/psf/codeofconduct/
-- Night gathers, and now my watch begins. It shall not end until my death.
I also agree with this analysis. I’ll just add that any discussion about optimization needs to also show some data, for let’s say both contrived examples (like this one IMHO) and as real-world examples as possible. Sometimes “obvious” optimizations fool you and things actually get slower. Or they don’t speed things up as much as you think they will. Optimizations usually involve adding complexity. I would like some sense of what we’d gain for the added complexity. Cheers, -Barry
On Nov 29, 2021, at 09:04, Serhiy Storchaka <storchaka@gmail.com> wrote:
29.11.21 18:36, Victor Stinner пише:
You should consider "no longer have to justify why it's not optimized" as a clear benefit of making this change :-) This optimization is proposed once a year for many years...
For me, any possible compilation-ahead optimization (which doesn't break the Python semantics) is worth it ;-) It's done once, possible even before the program is run for the first time, and then makes the code faster.
I consider the cost of rejecting such idea (this is actually the first time it was pushed so persistent) less than the cost of implementing and maintaining it. The proposed PR adds around 200 lines of code. It is almost 20% of the current size of ast_opt.c, it is not small. And just at first look I see a flaw in it -- it will emit a BytesWarning when compare strings and bytes. After fixing this the size will grow even more. And there may be other issues with this code which will be found months later. Later, when we rewrite the optimizer or change the structure of AST we will need to update this code too, with possibility to introduce new bugs.
On other hand, the benefit of this optimization is exact zero. It does not make code faster, because it optimizes the code not used in real programs.
_______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/LOIK6ZX4... Code of Conduct: http://python.org/psf/codeofconduct/
If someone wants to experiment such optimization, there is no need to modify the Python internal optimizer, it can be done externally: https://faster-cpython.readthedocs.io/ast_optimizer.html For example, I implemented many optimizations like constant propagation and loop unrolling in my old AST fatoptimizer project: https://github.com/vstinner/fatoptimizer/blob/master/doc/optimizations.rst Optimizing "2 < 3" becomes more interesting when other optimizations are implemented, like constant propagation or loop unrolling. So "x=2; if x < 10: ..." can be optimized for example. Victor
The code in the pull request is not updated and cannot be updated (though I seem to have fixed the bugs in the test in my modified CPython build). It won't merge, and I definitely believe it won't pass.
participants (15)
-
Antoine Pitrou -
Barry Warsaw -
Chris Angelico -
Christopher Barker -
Eric V. Smith -
Jeremiah Vivian -
Mark Shannon -
MRAB -
Pablo Galindo Salgado -
raymond.hettinger@gmail.com -
Rob Cliffe -
Serhiy Storchaka -
Skip Montanaro -
Steven D'Aprano -
Victor Stinner